diff options
355 files changed, 5752 insertions, 2491 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index f9cc1259a375..b9537b357517 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -50,6 +50,7 @@ aconfig_declarations_group { "android.hardware.devicestate.feature.flags-aconfig-java", "android.hardware.flags-aconfig-java", "android.hardware.radio.flags-aconfig-java", + "android.hardware.serial.flags-aconfig-java", "android.hardware.usb.flags-aconfig-java", "android.location.flags-aconfig-java", "android.media.codec-aconfig-java", @@ -1962,3 +1963,18 @@ java_aconfig_library { aconfig_declarations: "android.service.selinux.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Serial +aconfig_declarations { + name: "android.hardware.serial.flags-aconfig", + exportable: true, + package: "android.hardware.serial.flags", + container: "system", + srcs: ["core/java/android/hardware/serial/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.hardware.serial.flags-aconfig-java", + aconfig_declarations: "android.hardware.serial.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java index 00e1c1fdbf4b..3df708d1a5cd 100644 --- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java +++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java @@ -46,6 +46,7 @@ public class TracePerfTest { private static final String FOO = "foo"; private static final Category FOO_CATEGORY = new Category(FOO); + private static final Category UNREGISTERED_CATEGORY = new Category("unregistered"); private static PerfettoTrace.Session sPerfettoSession; @BeforeClass @@ -163,6 +164,30 @@ public class TracePerfTest { } } + @Test + public void testInstantPerfettoWithProtoUnregistered() { + PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive") + .beginProto() + .beginNested(2004 /* message_queue */) + .addField(1 /* sending_thread_name */, "foo") + .endNested() + .endProto() + .setTerminatingFlow(5) + .emit(); + + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive") + .beginProto() + .beginNested(2004 /* message_queue */) + .addField(1 /* sending_thread_name */, "foo") + .endNested() + .endProto() + .setTerminatingFlow(5) + .emit(); + } + } + private static TraceConfig getTraceConfig(String cat) { BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build(); TrackEventConfig trackEventConfig = TrackEventConfig diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java index da991624e685..d3e62d5351f0 100644 --- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java +++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java @@ -48,12 +48,17 @@ public class EvemuParser implements EventParser { private static class CommentAwareReader { private final LineNumberReader mReader; - private String mPreviousLine; - private String mNextLine; + /** The previous line of the file, or {@code null} if we're at the start of the file. */ + private @Nullable String mPreviousLine; + /** + * The next line of the file to be returned from {@link #peekLine()}, or {@code null} if we + * haven't peeked since the last {@link #advance()} or are at the end of the file. + */ + private @Nullable String mNextLine; + private boolean mAtEndOfFile = false; - CommentAwareReader(LineNumberReader in) throws IOException { + CommentAwareReader(LineNumberReader in) { mReader = in; - mNextLine = findNextLine(); } private @Nullable String findNextLine() throws IOException { @@ -61,7 +66,7 @@ public class EvemuParser implements EventParser { while (line != null && line.length() == 0) { String unstrippedLine = mReader.readLine(); if (unstrippedLine == null) { - // End of file. + mAtEndOfFile = true; return null; } line = stripComments(unstrippedLine); @@ -85,22 +90,28 @@ public class EvemuParser implements EventParser { * {@code null} if the end of the file is reached. However, it does not advance to the * next line of the file. */ - public @Nullable String peekLine() { + public @Nullable String peekLine() throws IOException { + if (mNextLine == null && !mAtEndOfFile) { + mNextLine = findNextLine(); + } return mNextLine; } /** Moves to the next line of the file. */ - public void advance() throws IOException { + public void advance() { mPreviousLine = mNextLine; - mNextLine = findNextLine(); + mNextLine = null; } public boolean isAtEndOfFile() { - return mNextLine == null; + return mAtEndOfFile; } - /** Returns the previous line, for error messages. */ - public String getPreviousLine() { + /** + * Returns the previous line, for error messages. Will be {@code null} if we're at the start + * of the file. + */ + public @Nullable String getPreviousLine() { return mPreviousLine; } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 95b9b49dae3d..42c60b0ba0da 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -18628,6 +18628,7 @@ package android.telephony.satellite { method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int); method @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatelliteDataOptimizedApps(); + method @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int getSatelliteDataSupportMode(int); method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int[] getSatelliteDisallowedReasons(); method @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int); method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -18710,6 +18711,10 @@ package android.telephony.satellite { field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2 field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_CONSTRAINED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_RESTRICTED = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_UNCONSTRAINED = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_UNKNOWN = -1; // 0xffffffff field public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7 field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3 field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2 diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index 2559bd036039..e2af7c03b5fa 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -1082,6 +1082,15 @@ public final class ApplicationStartInfo implements Parcelable { final ApplicationStartInfo o = (ApplicationStartInfo) other; + boolean intentEquals = true; + if (android.content.flags.Flags.intentSaveToXmlPackage()) { + if (mStartIntent == null) { + intentEquals = o.mStartIntent == null; + } else { + intentEquals = mStartIntent.filterEquals(o.mStartIntent); + } + } + return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid @@ -1095,14 +1104,16 @@ public final class ApplicationStartInfo implements Parcelable { && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped && mMonotonicCreationTimeMs == o.mMonotonicCreationTimeMs - && mStartComponent == o.mStartComponent; + && mStartComponent == o.mStartComponent + && intentEquals; } @Override public int hashCode() { return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState, mStartType, mLaunchMode, mPackageName, mProcessName, mStartupTimestampsNs, - mMonotonicCreationTimeMs, mStartComponent); + mMonotonicCreationTimeMs, mStartComponent, + android.content.flags.Flags.intentSaveToXmlPackage() ? mStartIntent : null); } private boolean timestampsEquals(@NonNull ApplicationStartInfo other) { diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 6f0eafe487af..d764c58b2b1e 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -385,3 +385,13 @@ flag { description: "Shows summarized notifications in the UI" bug: "390217880" } + +flag { + name: "nm_collapsed_lines" + namespace: "systemui" + description: "Shows 2 lines for collapsed notifications by default" + bug: "390217880" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 6e9dcf5a83a1..335448bf131e 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -79,6 +79,7 @@ import android.util.Log; import android.util.Pair; import android.util.Size; import android.view.Display; +import android.window.DesktopModeFlags; import com.android.internal.camera.flags.Flags; import com.android.internal.util.ArrayUtils; @@ -1685,7 +1686,7 @@ public final class CameraManager { */ public static int getRotationOverride(@Nullable Context context, @Nullable PackageManager packageManager, @Nullable String packageName) { - if (com.android.window.flags.Flags.enableCameraCompatForDesktopWindowing()) { + if (DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()) { return getRotationOverrideInternal(context, packageManager, packageName); } else { return shouldOverrideToPortrait(packageManager, packageName) diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 6c2ce3685b30..c41a5ce02e61 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -213,3 +213,12 @@ flag { is_fixed_read_only: true } +flag { + name: "fix_search_modifier_fallbacks" + namespace: "input" + description: "Fixes a bug in which fallbacks from Search based key combinations were not activating." + bug: "384113980" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/hardware/serial/flags/flags.aconfig b/core/java/android/hardware/serial/flags/flags.aconfig index d8244ba55fcc..bdb8b40af2bf 100644 --- a/core/java/android/hardware/serial/flags/flags.aconfig +++ b/core/java/android/hardware/serial/flags/flags.aconfig @@ -1,4 +1,4 @@ -package: "android.hardware.serial" +package: "android.hardware.serial.flags" container: "system" flag { diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index c21959b16fbb..c3c28b20d649 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -34,8 +34,6 @@ import android.util.Printer; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; -import com.android.internal.ravenwood.RavenwoodEnvironment; - import dalvik.annotation.optimization.NeverCompile; import java.io.FileDescriptor; @@ -1121,7 +1119,6 @@ public final class MessageQueue { msg.markInUse(); msg.arg1 = token; - incAndTraceMessageCount(msg, when); if (!enqueueMessageUnchecked(msg, when)) { Log.wtf(TAG_C, "Unexpected error while adding sync barrier!"); @@ -1137,7 +1134,6 @@ public final class MessageQueue { msg.markInUse(); msg.when = when; msg.arg1 = token; - incAndTraceMessageCount(msg, when); if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) { /* Message goes to tail of list */ diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java index 932836f8a050..8b96b89c949a 100644 --- a/core/java/android/os/PerfettoTrace.java +++ b/core/java/android/os/PerfettoTrace.java @@ -232,7 +232,8 @@ public final class PerfettoTrace { * @param eventName The event name to appear in the trace. */ public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) { - return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category) + return PerfettoTrackEventExtra.builder(category.isEnabled()) + .init(PERFETTO_TE_TYPE_INSTANT, category) .setEventName(eventName); } @@ -243,7 +244,8 @@ public final class PerfettoTrace { * @param eventName The event name to appear in the trace. */ public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) { - return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category) + return PerfettoTrackEventExtra.builder(category.isEnabled()) + .init(PERFETTO_TE_TYPE_SLICE_BEGIN, category) .setEventName(eventName); } @@ -253,7 +255,8 @@ public final class PerfettoTrace { * @param category The perfetto category. */ public static PerfettoTrackEventExtra.Builder end(Category category) { - return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category); + return PerfettoTrackEventExtra.builder(category.isEnabled()) + .init(PERFETTO_TE_TYPE_SLICE_END, category); } /** @@ -263,7 +266,8 @@ public final class PerfettoTrace { * @param value The value of the counter. */ public static PerfettoTrackEventExtra.Builder counter(Category category, long value) { - return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category) + return PerfettoTrackEventExtra.builder(category.isEnabled()) + .init(PERFETTO_TE_TYPE_COUNTER, category) .setCounter(value); } @@ -286,7 +290,8 @@ public final class PerfettoTrace { * @param value The value of the counter. */ public static PerfettoTrackEventExtra.Builder counter(Category category, double value) { - return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category) + return PerfettoTrackEventExtra.builder(category.isEnabled()) + .init(PERFETTO_TE_TYPE_COUNTER, category) .setCounter(value); } diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java index 07b44a87ef88..5fc7cf7be246 100644 --- a/core/java/android/os/PerfettoTrackEventExtra.java +++ b/core/java/android/os/PerfettoTrackEventExtra.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; public final class PerfettoTrackEventExtra { private static final boolean DEBUG = false; private static final int DEFAULT_EXTRA_CACHE_SIZE = 5; + private static final Builder NO_OP_BUILDER = new Builder(/* extra= */ null, /* isCategoryEnabled= */ false); private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra = new ThreadLocal<PerfettoTrackEventExtra>() { @Override @@ -153,8 +154,8 @@ public final class PerfettoTrackEventExtra { private Builder mParent; private FieldContainer mCurrentContainer; - private boolean mIsCategoryEnabled; + private final boolean mIsCategoryEnabled; private final CounterInt64 mCounterInt64; private final CounterDouble mCounterDouble; private final Proto mProto; @@ -175,24 +176,29 @@ public final class PerfettoTrackEventExtra { private final Pool<Builder> mBuilderCache; private Builder() { - mExtra = sTrackEventExtra.get(); - mNamedTrackCache = mExtra.mNamedTrackCache; - mCounterTrackCache = mExtra.mCounterTrackCache; - mArgInt64Cache = mExtra.mArgInt64Cache; - mArgDoubleCache = mExtra.mArgDoubleCache; - mArgBoolCache = mExtra.mArgBoolCache; - mArgStringCache = mExtra.mArgStringCache; - mFieldInt64Cache = mExtra.mFieldInt64Cache; - mFieldDoubleCache = mExtra.mFieldDoubleCache; - mFieldStringCache = mExtra.mFieldStringCache; - mFieldNestedCache = mExtra.mFieldNestedCache; - mBuilderCache = mExtra.mBuilderCache; - - mCounterInt64 = mExtra.getCounterInt64(); - mCounterDouble = mExtra.getCounterDouble(); - mProto = mExtra.getProto(); - mFlow = mExtra.getFlow(); - mTerminatingFlow = mExtra.getTerminatingFlow(); + this(sTrackEventExtra.get(), true); + } + + public Builder(PerfettoTrackEventExtra extra, boolean isCategoryEnabled) { + mIsCategoryEnabled = isCategoryEnabled; + mExtra = extra; + mNamedTrackCache = mExtra == null ? null : mExtra.mNamedTrackCache; + mCounterTrackCache = mExtra == null ? null : mExtra.mCounterTrackCache; + mArgInt64Cache = mExtra == null ? null : mExtra.mArgInt64Cache; + mArgDoubleCache = mExtra == null ? null : mExtra.mArgDoubleCache; + mArgBoolCache = mExtra == null ? null : mExtra.mArgBoolCache; + mArgStringCache = mExtra == null ? null : mExtra.mArgStringCache; + mFieldInt64Cache = mExtra == null ? null : mExtra.mFieldInt64Cache; + mFieldDoubleCache = mExtra == null ? null : mExtra.mFieldDoubleCache; + mFieldStringCache = mExtra == null ? null : mExtra.mFieldStringCache; + mFieldNestedCache = mExtra == null ? null : mExtra.mFieldNestedCache; + mBuilderCache = mExtra == null ? null : mExtra.mBuilderCache; + + mCounterInt64 = mExtra == null ? null : mExtra.getCounterInt64(); + mCounterDouble = mExtra == null ? null : mExtra.getCounterDouble(); + mProto = mExtra == null ? null : mExtra.getProto(); + mFlow = mExtra == null ? null : mExtra.getFlow(); + mTerminatingFlow = mExtra == null ? null : mExtra.getTerminatingFlow(); } /** @@ -214,6 +220,10 @@ public final class PerfettoTrackEventExtra { * Initialize the builder for a new trace event. */ public Builder init(int traceType, PerfettoTrace.Category category) { + if (!mIsCategoryEnabled) { + return this; + } + mTraceType = traceType; mCategory = category; mEventName = ""; @@ -225,7 +235,7 @@ public final class PerfettoTrackEventExtra { mExtra.reset(); // Reset after on init in case the thread created builders without calling emit - return initInternal(this, null, category.isEnabled()); + return initInternal(this, null); } /** @@ -529,7 +539,7 @@ public final class PerfettoTrackEventExtra { } mProto.clearFields(); mExtra.addPerfettoPointer(mProto); - return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto, true); + return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto); } /** @@ -560,7 +570,7 @@ public final class PerfettoTrackEventExtra { FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier); field.setId(id); mExtra.addPerfettoPointer(mCurrentContainer, field); - return mBuilderCache.get(sBuilderSupplier).initInternal(this, field, true); + return mBuilderCache.get(sBuilderSupplier).initInternal(this, field); } /** @@ -577,11 +587,9 @@ public final class PerfettoTrackEventExtra { } - private Builder initInternal(Builder parent, FieldContainer field, - boolean isCategoryEnabled) { + private Builder initInternal(Builder parent, FieldContainer field) { mParent = parent; mCurrentContainer = field; - mIsCategoryEnabled = isCategoryEnabled; mIsBuilt = false; return this; @@ -613,9 +621,12 @@ public final class PerfettoTrackEventExtra { /** * Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}. */ - public static Builder builder() { - return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null, - false); + public static Builder builder(boolean isCategoryEnabled) { + if (isCategoryEnabled) { + return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier) + .initInternal(null, null); + } + return NO_OP_BUILDER; } private final RingBuffer<NamedTrack> mNamedTrackCache = diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b7e296228e45..e7bca1419418 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13317,10 +13317,18 @@ public final class Settings { public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package"; /** - * Inetger property which determines whether advanced protection is on or not. + * Integer property which determines whether advanced protection is on or not. * @hide */ public static final String ADVANCED_PROTECTION_MODE = "advanced_protection_mode"; + + /** + * Integer property which determines whether advanced protection USB data protection + * feature is on or not. + * + * @hide + */ + public static final String AAPM_USB_DATA_PROTECTION = "aapm_usb_data_protection"; } /** diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java index 2feb44ffc3ae..892b80bd0213 100644 --- a/core/java/android/view/OrientationEventListener.java +++ b/core/java/android/view/OrientationEventListener.java @@ -26,8 +26,7 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.util.Log; - -import com.android.window.flags.Flags; +import android.window.DesktopModeFlags; /** * Helper class for receiving notifications from the SensorManager when @@ -77,9 +76,10 @@ public abstract class OrientationEventListener { mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (mSensor != null) { // Create listener only if sensors do exist. - mSensorEventListener = Flags.enableCameraCompatForDesktopWindowing() - ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl()) - : new SensorEventListenerImpl(); + mSensorEventListener = + DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue() + ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl()) + : new SensorEventListenerImpl(); } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 97cf8fc748e8..8944c3fa0b19 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -120,7 +120,7 @@ public class WindowManagerImpl implements WindowManager { this(context, null /* parentWindow */, null /* clientToken */); } - private WindowManagerImpl(Context context, Window parentWindow, + public WindowManagerImpl(Context context, Window parentWindow, @Nullable IBinder windowContextToken) { mContext = context; mParentWindow = parentWindow; diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 4bd0d97a54b0..d5120ffb4de6 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -48,6 +48,8 @@ public enum DesktopModeFlags { DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true), ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true), ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true), + ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION( + Flags::enableCameraCompatForDesktopWindowing, true), ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(Flags::enableCaptionCompatInsetForceConsumption, true), ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS( @@ -83,7 +85,7 @@ public enum DesktopModeFlags { ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX( Flags::enableDesktopWindowingEnterTransitionBugfix, true), ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX( - Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, false), + Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, true), ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX( Flags::enableDesktopWindowingExitTransitionsBugfix, true), ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true), diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java index 5d66b3c10197..74153157df4d 100644 --- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java +++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java @@ -142,14 +142,23 @@ public class DisplayResolutionTracker { public interface DisplayInterface { /** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */ static DisplayInterface getDefault(@Nullable Handler handler) { + long displayEventsToBeSubscribed; + if (com.android.server.display.feature.flags.Flags + .displayListenerPerformanceImprovements() + && com.android.server.display.feature.flags.Flags + .delayImplicitRrRegistrationUntilRrAccessed()) { + displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED; + } else { + displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE; + } DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance(); return new DisplayInterface() { @Override public void registerDisplayListener(DisplayManager.DisplayListener listener) { - manager.registerDisplayListener(listener, handler, - DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE, + manager.registerDisplayListener(listener, handler, displayEventsToBeSubscribed, ActivityThread.currentPackageName()); } diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index 6f7e5ad51b89..7cecc39dacde 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -66,6 +66,7 @@ import com.android.internal.protolog.IProtoLogConfigurationService.RegisterClien import com.android.internal.protolog.common.ILogger; import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.InvalidFormatStringException; import com.android.internal.protolog.common.LogDataType; import com.android.internal.protolog.common.LogLevel; @@ -207,7 +208,12 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen @Override public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object... args) { - log(logLevel, group, new Message(messageString), args); + try { + log(logLevel, group, new Message(messageString), args); + } catch (InvalidFormatStringException e) { + Slog.e(LOG_TAG, "Invalid protolog string format", e); + log(logLevel, group, new Message("INVALID MESSAGE"), new Object[0]); + } } /** @@ -831,7 +837,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen this.mMessageString = null; } - private Message(@NonNull String messageString) { + private Message(@NonNull String messageString) throws InvalidFormatStringException { this.mMessageHash = null; final List<Integer> argTypes = LogDataType.parseFormatString(messageString); this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes); diff --git a/core/java/com/android/internal/protolog/common/LogDataType.java b/core/java/com/android/internal/protolog/common/LogDataType.java index c05824a58a77..10b07d9387de 100644 --- a/core/java/com/android/internal/protolog/common/LogDataType.java +++ b/core/java/com/android/internal/protolog/common/LogDataType.java @@ -86,8 +86,8 @@ public class LogDataType { case '%': break; default: - throw new InvalidFormatStringException("Invalid format string field" - + " %${messageString[i + 1]}"); + throw new InvalidFormatStringException("Invalid Protolog message format in " + + "\"" + messageString + "\" at index " + i + "."); } i += 2; } else { diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 3da19220248b..822ef34ad925 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -401,9 +401,19 @@ public class ConversationLayout extends FrameLayout @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync") public void setIsCollapsed(boolean isCollapsed) { mIsCollapsed = isCollapsed; - mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed - ? TextUtils.isEmpty(mSummarizedContent) ? 1 : MAX_SUMMARIZATION_LINES - : Integer.MAX_VALUE); + int maxLines = Integer.MAX_VALUE; + if (isCollapsed) { + if (!TextUtils.isEmpty(mSummarizedContent)) { + maxLines = MAX_SUMMARIZATION_LINES; + } else { + if (android.app.Flags.nmCollapsedLines()) { + maxLines = 2; + } else { + maxLines = 1; + } + } + } + mMessagingLinearLayout.setMaxDisplayedLines(maxLines); updateExpandButton(); updateContentEndPaddings(); } @@ -1177,7 +1187,9 @@ public class ConversationLayout extends FrameLayout nameOverride = mNameReplacement; } newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed); - newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent)); + newGroup.setSingleLine(mIsCollapsed + ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent) + : false); newGroup.setIsCollapsed(mIsCollapsed); newGroup.setSender(sender, nameOverride); newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner); diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 4cc4b38f95a5..f9c8228e455d 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -157,6 +157,10 @@ public class MessagingLayout extends FrameLayout @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync") public void setIsCollapsed(boolean isCollapsed) { mIsCollapsed = isCollapsed; + if (mIsCollapsed) { + mMessagingLinearLayout.setMaxDisplayedLines( + android.app.Flags.nmCollapsedLines() ? 2 : 1); + } } /** @@ -549,7 +553,9 @@ public class MessagingLayout extends FrameLayout if (sender != mUser && mNameReplacement != null) { nameOverride = mNameReplacement; } - newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent)); + newGroup.setSingleLine(mIsCollapsed + ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent) + : false); newGroup.setShowingAvatar(!mIsCollapsed); newGroup.setIsCollapsed(mIsCollapsed); newGroup.setSender(sender, nameOverride); diff --git a/core/java/com/android/server/servicewatcher/OWNERS b/core/java/com/android/server/servicewatcher/OWNERS index ced619f05f1d..dbc598e1547f 100644 --- a/core/java/com/android/server/servicewatcher/OWNERS +++ b/core/java/com/android/server/servicewatcher/OWNERS @@ -1,5 +1,5 @@ # Bug component: 25692 -sooniln@google.com +dnchrist@google.com wyattriley@google.com diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1a74fe6719e3..7bb799a24ef1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3209,6 +3209,10 @@ Note that HSUM devices without this enabled will not automatically have a main user. --> <bool name="config_isMainUserPermanentAdmin">true</bool> + <!-- Whether all secondary users (and the system user) are eligible to have profiles. + If false, only the MainUser is eligible to have profiles. --> + <bool name="config_supportProfilesOnNonMainUser">false</bool> + <!-- Whether switch to headless system user is allowed. If allowed, headless system user can run in the foreground even though it is not a full user. --> <bool name="config_canSwitchToHeadlessSystemUser">false</bool> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 849ca2882889..1d40110dc7ca 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -262,7 +262,22 @@ carrier_supported_satellite_services_per_provider_bundle does not support them. --> <string-array name="config_satellite_providers" translatable="false"> + <!-- T-Mobile - USA --> <item>"310830"</item> + <!-- Entel Chile - Chile --> + <item>"73029"</item> + <!-- KDDI - Japan --> + <item>"44055"</item> + <!-- Kyivstar - Ukraine --> + <item>"255707"</item> + <!-- One NZ - New Zealand --> + <item>"53013"</item> + <!-- Optus - Australia --> + <item>"50559"</item> + <!-- Rogers - Canada --> + <item>"302723"</item> + <!-- Telstra - Australia --> + <item>"50511"</item> </string-array> <java-symbol type="array" name="config_satellite_providers" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a37ca2847638..2dc5687a1253 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -363,6 +363,7 @@ <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" /> <java-symbol type="bool" name="config_useFixedVolume" /> <java-symbol type="bool" name="config_isMainUserPermanentAdmin"/> + <java-symbol type="bool" name="config_supportProfilesOnNonMainUser"/> <java-symbol type="bool" name="config_canSwitchToHeadlessSystemUser"/> <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_enableMultipleAdmins"/> diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml index dd1a1b1dca13..75ec2ab9f6f9 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml +++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml @@ -18,15 +18,15 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <item android:id="@+id/indicator_solid"> <shape android:shape="rectangle"> - <solid android:color="@androidprv:color/materialColorPrimaryContainer" /> + <solid android:color="@androidprv:color/materialColorPrimaryFixed" /> <corners android:radius="28dp" /> </shape> </item> <item android:id="@+id/indicator_stroke"> <shape android:shape="rectangle"> <corners android:radius="28dp" /> - <stroke android:width="1dp" - android:color="@androidprv:color/materialColorPrimaryContainer"/> + <stroke android:width="2dp" + android:color="@androidprv:color/materialColorPrimaryFixed"/> </shape> </item> </layer-list> diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 8d18f959951b..5732fc936b47 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -68,8 +68,6 @@ <color name="desktop_mode_caption_button_on_hover_light">#11000000</color> <color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color> <color name="desktop_mode_caption_button">#00000000</color> - <color name="tiling_divider_background_light">#C9C7B6</color> - <color name="tiling_divider_background_dark">#4A4739</color> <color name="tiling_handle_background_light">#000000</color> <color name="tiling_handle_background_dark">#FFFFFF</color> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 733f3bb8d6d0..ca18c97f9127 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -304,6 +304,8 @@ <dimen name="bubble_transform_area_width">140dp</dimen> <!-- Width of the box at the corner of the screen where drag leads to app moving to bubble --> <dimen name="bubble_transform_area_height">140dp</dimen> + <!-- How much elevation a bubble ui needs when dragged, must be above drop target & dismiss. --> + <dimen name="dragged_bubble_elevation">3dp</dimen> <!-- Bottom and end margin for compat buttons. --> <dimen name="compat_button_margin">24dp</dimen> diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml index 3b504cf713f1..74b6023bde36 100644 --- a/libs/WindowManager/Shell/shared/res/values/dimen.xml +++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml @@ -39,9 +39,9 @@ <dimen name="drag_zone_v_split_from_expanded_view_height_fold_short">100dp</dimen> <!-- Bubble drop target dimensions --> - <dimen name="drop_target_elevation">1dp</dimen> + <dimen name="drop_target_elevation">2dp</dimen> <dimen name="drop_target_radius">28dp</dimen> - <dimen name="drop_target_stroke">1dp</dimen> + <dimen name="drop_target_stroke">2dp</dimen> <dimen name="drop_target_full_screen_padding">20dp</dimen> <dimen name="drop_target_desktop_window_padding_small">100dp</dimen> <dimen name="drop_target_desktop_window_padding_large">130dp</dimen> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt index 73277310ffe4..df101fe44b75 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt @@ -30,15 +30,15 @@ import com.android.wm.shell.shared.R class DropTargetView(context: Context) : View(context) { private val rectPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer) + color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed) style = Paint.Style.FILL alpha = (0.35f * 255).toInt() } private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer) + color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed) style = Paint.Style.STROKE - strokeWidth = 1.dpToPx() + strokeWidth = 2.dpToPx() } private val cornerRadius = 28.dpToPx() 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 e5a4cd034e72..a9224b02ad31 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 @@ -451,6 +451,6 @@ public class DesktopModeStatus { pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1)); pw.print(innerPrefix); pw.print("showAppHandle config override="); - pw.print(overridesShowAppHandle(context)); + pw.println(overridesShowAppHandle(context)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java index 8e78686ac13d..8e3dc4c36c1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java @@ -66,7 +66,7 @@ public class SizeChangeAnimation { * The maximum of stretching applied to any surface during interpolation (since the animation * is a combination of stretching/cropping/fading). */ - private static final float SCALE_FACTOR = 0.7f; + private static final float DEFAULT_SCALE_FACTOR = 0.7f; /** * Since this animation is made of several sub-animations, we want to pre-arrange the @@ -82,13 +82,27 @@ public class SizeChangeAnimation { */ private static final int ANIMATION_RESOLUTION = 1000; + /** + * Initialize a size-change animation from start to end bounds + */ public SizeChangeAnimation(Rect startBounds, Rect endBounds) { - this(startBounds, endBounds, 1f); + this(startBounds, endBounds, 1f, DEFAULT_SCALE_FACTOR); } - public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale) { - mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale); - mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds); + /** + * Initialize a size-change animation from start to end bounds. + * <p> + * Allows specifying the initial scale factor, {@code initialScale}, that is applied to the + * start bounds. This can be useful for example when a task is scaled down when the size change + * animation starts. + * <p> + * By default the max scale applied to any surface is {@link #DEFAULT_SCALE_FACTOR}. Use + * {@code scaleFactor} to override it. + */ + public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale, + float scaleFactor) { + mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale, scaleFactor); + mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds, scaleFactor); } /** @@ -172,15 +186,15 @@ public class SizeChangeAnimation { /** Animation for the whole container (snapshot is inside this container). */ private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds, - float initialScale) { + float initialScale, float scaleFactor) { final long duration = ANIMATION_RESOLUTION; boolean growing = endBounds.width() - startBounds.width() + endBounds.height() - startBounds.height() >= 0; - long scalePeriod = (long) (duration * SCALE_FACTOR); - float startScaleX = SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width() - + (1.f - SCALE_FACTOR); - float startScaleY = SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height() - + (1.f - SCALE_FACTOR); + long scalePeriod = (long) (duration * scaleFactor); + float startScaleX = scaleFactor * ((float) startBounds.width()) / endBounds.width() + + (1.f - scaleFactor); + float startScaleY = scaleFactor * ((float) startBounds.height()) / endBounds.height() + + (1.f - scaleFactor); final AnimationSet animSet = new AnimationSet(true); final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1); @@ -218,15 +232,16 @@ public class SizeChangeAnimation { } /** The snapshot surface is assumed to be a child of the container surface. */ - private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds) { + private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds, + float scaleFactor) { final long duration = ANIMATION_RESOLUTION; boolean growing = endBounds.width() - startBounds.width() + endBounds.height() - startBounds.height() >= 0; - long scalePeriod = (long) (duration * SCALE_FACTOR); - float endScaleX = 1.f / (SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width() - + (1.f - SCALE_FACTOR)); - float endScaleY = 1.f / (SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height() - + (1.f - SCALE_FACTOR)); + long scalePeriod = (long) (duration * scaleFactor); + float endScaleX = 1.f / (scaleFactor * ((float) startBounds.width()) / endBounds.width() + + (1.f - scaleFactor)); + float endScaleY = 1.f / (scaleFactor * ((float) startBounds.height()) / endBounds.height() + + (1.f - scaleFactor)); AnimationSet snapAnimSet = new AnimationSet(true); // Animation for the "old-state" snapshot that is atop the task. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index fa22a3961002..b89bfd5c969e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -596,11 +596,11 @@ public class BubbleBarAnimationHelper { final Size size = getExpandedViewSize(); Point position = getExpandedViewRestPosition(size); - final SizeChangeAnimation sca = - new SizeChangeAnimation( - new Rect(origBounds.left - position.x, origBounds.top - position.y, - origBounds.right - position.x, origBounds.bottom - position.y), - new Rect(0, 0, size.getWidth(), size.getHeight()), origScale); + Rect startBounds = new Rect(origBounds.left - position.x, origBounds.top - position.y, + origBounds.right - position.x, origBounds.bottom - position.y); + Rect endBounds = new Rect(0, 0, size.getWidth(), size.getHeight()); + final SizeChangeAnimation sca = new SizeChangeAnimation(startBounds, endBounds, + origScale, /* scaleFactor= */ 1f); sca.initialize(bbev, taskLeash, snapshot, startT); Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt index 35435569d8b1..44d859dfb9ba 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt @@ -17,9 +17,11 @@ package com.android.wm.shell.bubbles.bar import android.annotation.SuppressLint +import android.content.Context import android.view.MotionEvent import android.view.View import androidx.annotation.VisibleForTesting +import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner import com.android.wm.shell.shared.bubbles.BubbleBarLocation import com.android.wm.shell.shared.bubbles.DismissView @@ -32,6 +34,7 @@ import com.android.wm.shell.shared.magnetictarget.MagnetizedObject /** Controller for handling drag interactions with [BubbleBarExpandedView] */ @SuppressLint("ClickableViewAccessibility") class BubbleBarExpandedViewDragController( + private val context: Context, private val expandedView: BubbleBarExpandedView, private val dismissView: DismissView, private val animationHelper: BubbleBarAnimationHelper, @@ -54,6 +57,8 @@ class BubbleBarExpandedViewDragController( MagnetizedObject.magnetizeView(expandedView) private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget + private val draggedBubbleElevation: Float + init { magnetizedExpandedView.magnetListener = MagnetListener() magnetizedExpandedView.animateStuckToTarget = @@ -70,6 +75,8 @@ class BubbleBarExpandedViewDragController( MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width) magnetizedExpandedView.addTarget(magnetizedDismissTarget) + draggedBubbleElevation = context.resources.getDimension( + R.dimen.dragged_bubble_elevation) val dragMotionEventHandler = HandleDragListener() expandedView.handleView.setOnTouchListener { view, event -> @@ -103,6 +110,7 @@ class BubbleBarExpandedViewDragController( override fun onDown(v: View, ev: MotionEvent): Boolean { // While animating, don't allow new touch events if (expandedView.isAnimating) return false + expandedView.z = draggedBubbleElevation if (dropTargetManager != null && dragZoneFactory != null) { val draggedObject = DraggedObject.ExpandedView( if (bubblePositioner.isBubbleBarOnLeft) { @@ -154,11 +162,13 @@ class BubbleBarExpandedViewDragController( velX: Float, velY: Float, ) { + v.translationZ = 0f finishDrag() } override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) { isStuckToDismiss = false + v.translationZ = 0f finishDrag() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 2cc9387bd1e9..dd86725f7944 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -349,6 +349,7 @@ public class BubbleBarLayerView extends FrameLayout } }; mDragController = new BubbleBarExpandedViewDragController( + mContext, mExpandedView, mDismissView, mAnimationHelper, 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 97184c859d4d..46f6e40ec5e8 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 @@ -287,7 +287,13 @@ public class DisplayController { ? mContext : mContext.createDisplayContext(display); final Context context = perDisplayContext.createConfigurationContext(newConfig); - dr.setDisplayLayout(context, new DisplayLayout(context, display)); + final DisplayLayout displayLayout = new DisplayLayout(context, display); + if (mDisplayTopology != null) { + displayLayout.setGlobalBoundsDp( + mDisplayTopology.getAbsoluteBounds().get( + displayId, displayLayout.globalBoundsDp())); + } + dr.setDisplayLayout(context, displayLayout); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java index cf0ecae7c815..a1e7ff04347f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java @@ -106,31 +106,37 @@ public class DividerRoundedCorner extends View { * of non split screen. * * @param isSplitScreen Whether the divider is used by split screen or tiling. - * @param isDarkMode Whether the mode is ui dark mode. + * @param color Rounded corner color. */ - public void setup(boolean isSplitScreen, boolean isDarkMode) { + public void setup(boolean isSplitScreen, int color) { mIsSplitScreen = isSplitScreen; if (!isSplitScreen) { - mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode)); + mDividerBarBackground.setColor(color); } } /** - * Notifies the divider of ui mode change. + * Notifies the divider of ui mode change and provides a new color. * - * @param isDarkMode Whether the mode is ui dark mode. + * @param color The new divider rounded corner color. */ - public void onUiModeChange(boolean isDarkMode) { + public void onUiModeChange(int color) { if (!mIsSplitScreen) { - mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode)); + mDividerBarBackground.setColor(color); invalidate(); } } - private int getTilingHandleColor(boolean isDarkMode) { - return isDarkMode ? getResources().getColor( - R.color.tiling_divider_background_dark, null /* theme */) : getResources().getColor( - R.color.tiling_divider_background_light, null /* theme */); + /** + * Notifies rounded corner view of color change. + * + * @param color The new divider rounded corner color. + */ + public void onCornerColorChange(int color) { + if (!mIsSplitScreen) { + mDividerBarBackground.setColor(color); + invalidate(); + } } /** 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 0edab006cb66..5fbbb0bf1e78 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 @@ -90,6 +90,7 @@ import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler; import com.android.wm.shell.desktopmode.DesktopDisplayModeController; +import com.android.wm.shell.desktopmode.DesktopImeHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler; import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler; @@ -1526,6 +1527,18 @@ public abstract class WMShellModule { mainHandler)); } + @WMSingleton + @Provides + static Optional<DesktopImeHandler> provideDesktopImeHandler( + DisplayImeController displayImeController, + Context context, + ShellInit shellInit) { + if (!DesktopModeStatus.canEnterDesktopMode(context)) { + return Optional.empty(); + } + return Optional.of(new DesktopImeHandler(displayImeController, shellInit)); + } + // // App zoom out // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt new file mode 100644 index 000000000000..93ba71a17937 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.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.desktopmode + +import android.view.SurfaceControl +import com.android.window.flags.Flags +import com.android.wm.shell.common.DisplayImeController +import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT +import com.android.wm.shell.sysui.ShellInit + +/** Handles the interactions between IME and desktop tasks */ +class DesktopImeHandler( + private val displayImeController: DisplayImeController, + shellInit: ShellInit, +) : DisplayImeController.ImePositionProcessor { + + init { + shellInit.addInitCallback(::onInit, this) + } + + private fun onInit() { + if (Flags.enableDesktopImeBugfix()) { + displayImeController.addPositionProcessor(this) + } + } + + override fun onImeStartPositioning( + displayId: Int, + hiddenTop: Int, + shownTop: Int, + showing: Boolean, + isFloating: Boolean, + t: SurfaceControl.Transaction?, + ): Int { + return IME_ANIMATION_DEFAULT + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt index b9cb32d8a14f..e0300d688379 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt @@ -167,7 +167,35 @@ class DesktopModeUiEventLogger( @UiEvent(doc = "Exit desktop mode education tooltip on the app header menu is clicked") EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_CLICKED(2104), @UiEvent(doc = "Exit desktop mode education tooltip is dismissed by the user") - EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105); + EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105), + @UiEvent(doc = "A11y service opened app handle menu by selecting handle from fullscreen") + A11Y_APP_HANDLE_MENU_OPENED(2156), + @UiEvent(doc = "A11y service opened app handle menu through Switch Access actions menu ") + A11Y_SYSTEM_ACTION_APP_HANDLE_MENU(2157), + @UiEvent(doc = "A11y service selected desktop mode from app handle menu") + A11Y_APP_HANDLE_MENU_DESKTOP_VIEW(2158), + @UiEvent(doc = "A11y service selected fullscreen mode from app handle menu") + A11Y_APP_HANDLE_MENU_FULLSCREEN(2159), + @UiEvent(doc = "A11y service selected split screen mode from app handle menu") + A11Y_APP_HANDLE_MENU_SPLIT_SCREEN(2160), + @UiEvent(doc = "A11y service selected maximize/restore button from app header") + A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON(2161), + @UiEvent(doc = "A11y service selected minimize button from app header") + A11Y_APP_WINDOW_MINIMIZE_BUTTON(2162), + @UiEvent(doc = "A11y service selected close button from app header") + A11Y_APP_WINDOW_CLOSE_BUTTON(2163), + @UiEvent(doc = "A11y service selected maximize button from app header maximize menu") + A11Y_MAXIMIZE_MENU_MAXIMIZE(2164), + @UiEvent(doc = "A11y service selected resize left button from app header maximize menu") + A11Y_MAXIMIZE_MENU_RESIZE_LEFT(2165), + @UiEvent(doc = "A11y service selected resize right button from app header maximize menu") + A11Y_MAXIMIZE_MENU_RESIZE_RIGHT(2166), + @UiEvent(doc = "A11y service triggered a11y action to maximize/restore app window") + A11Y_ACTION_MAXIMIZE_RESTORE(2167), + @UiEvent(doc = "A11y service triggered a11y action to resize app window left") + A11Y_ACTION_RESIZE_LEFT(2168), + @UiEvent(doc = "A11y service triggered a11y action to resize app window right") + A11Y_ACTION_RESIZE_RIGHT(2169); override fun getId(): Int = mId } 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 e77acfb805a6..25fdbb348356 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 @@ -1112,6 +1112,7 @@ class DesktopRepository( internal fun dump(pw: PrintWriter, prefix: String) { val innerPrefix = "$prefix " pw.println("${prefix}DesktopRepository") + pw.println("${innerPrefix}userId=$userId") dumpDesktopTaskData(pw, innerPrefix) pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}") pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}") @@ -1298,6 +1299,7 @@ class DesktopRepository( deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence() override fun remove(deskId: Int) { + setDeskInactive(deskId) deskByDisplayId[deskId]?.clear() } @@ -1397,6 +1399,7 @@ class DesktopRepository( desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence() override fun remove(deskId: Int) { + setDeskInactive(deskId) desktopDisplays.forEach { _, display -> display.orderedDesks.removeIf { it.deskId == deskId } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt index c10752d36bf9..72758bd538d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.pm.UserInfo import android.os.UserManager import android.util.SparseArray +import android.window.DesktopExperienceFlags import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM import androidx.core.util.forEach import com.android.internal.protolog.ProtoLog @@ -68,7 +69,10 @@ class DesktopUserRepositories( if (DesktopModeStatus.canEnterDesktopMode(context)) { shellInit.addInitCallback(::onInit, this) } - if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) { + if ( + ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() && + !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue + ) { userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id } } } @@ -76,6 +80,13 @@ class DesktopUserRepositories( private fun onInit() { repositoryInitializer.initialize(this) shellController.addUserChangeListener(this) + if ( + ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() && + DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue + ) { + userId = ActivityManager.getCurrentUser() + userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id } + } } /** Returns [DesktopRepository] for the parent user id. */ @@ -97,10 +108,10 @@ class DesktopUserRepositories( /** Dumps [DesktopRepository] for each user. */ fun dump(pw: PrintWriter, prefix: String) { - desktopRepoByUserId.forEach { key, value -> - pw.println("${prefix}userId=$key") - value.dump(pw, prefix) - } + val innerPrefix = "$prefix " + pw.println("${prefix}DesktopUserRepositories:") + pw.println("${innerPrefix}currentUserId=$userId") + desktopRepoByUserId.forEach { key, value -> value.dump(pw, innerPrefix) } } override fun onUserChanged(newUserId: Int, userContext: Context) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt index 8ce624e103ef..bf687f2e4f3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.os.Handler import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.sysui.ShellInit @@ -28,6 +29,7 @@ import java.util.function.Supplier /** * This supplies the package name of default home in an efficient way. The query to package manager * only executes on initialization and when the preferred activity (e.g. default home) is changed. + * Note that this returns null package name if the default home is setup wizard. */ class DefaultHomePackageSupplier( private val context: Context, @@ -36,6 +38,7 @@ class DefaultHomePackageSupplier( ) : BroadcastReceiver(), Supplier<String?> { private var defaultHomePackage: String? = null + private var isSetupWizard: Boolean = false init { shellInit.addInitCallback({ onInit() }, this) @@ -52,6 +55,14 @@ class DefaultHomePackageSupplier( private fun updateDefaultHomePackage(): String? { defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName + isSetupWizard = + defaultHomePackage != null && + context.packageManager.resolveActivity( + Intent() + .setPackage(defaultHomePackage) + .addCategory(Intent.CATEGORY_SETUP_WIZARD), + PackageManager.MATCH_SYSTEM_ONLY, + ) != null return defaultHomePackage } @@ -60,6 +71,7 @@ class DefaultHomePackageSupplier( } override fun get(): String? { + if (isSetupWizard) return null return defaultHomePackage ?: updateDefaultHomePackage() } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index b3c1a92f5e1d..d6084138b7e7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -663,6 +663,9 @@ public class DragLayout extends LinearLayout mSession = null; } }); + // notify bubbles of drag cancel + mCurrentBubbleBarTarget = null; + mBubbleBarDragListener.onItemDraggedOutsideBubbleBarDropZone(); // Reset the state if we previously force-ignore the bottom margin mDropZoneView1.setForceIgnoreBottomMargin(false); mDropZoneView2.setForceIgnoreBottomMargin(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index bf5800330979..825e8c70fa77 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -59,7 +59,6 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.internal.jank.Cuj.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION; -import static com.android.internal.policy.TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CHANGE; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE; @@ -116,6 +115,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.TransitionUtil; +import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.sysui.ShellInit; import java.util.ArrayList; @@ -125,6 +125,7 @@ import java.util.function.Consumer; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private static final int MAX_ANIMATION_DURATION = 3000; + private static final int SIZE_CHANGE_ANIMATION_DURATION = 400; private final TransactionPool mTransactionPool; private final DisplayController mDisplayController; @@ -779,15 +780,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private void startBoundsChangeAnimation(@NonNull SurfaceControl.Transaction startT, @NonNull ArrayList<Animator> animations, @NonNull TransitionInfo.Change change, @NonNull Runnable finishCb, @NonNull ShellExecutor mainExecutor) { - final SizeChangeAnimation sca = - new SizeChangeAnimation(change.getStartAbsBounds(), change.getEndAbsBounds()); + final SizeChangeAnimation sca = new SizeChangeAnimation(change.getStartAbsBounds(), + change.getEndAbsBounds(), /* initialScale= */ 1f, /* scaleFactor= */ 1f); sca.initialize(change.getLeash(), change.getSnapshot(), startT); final ValueAnimator va = sca.buildAnimator(change.getLeash(), change.getSnapshot(), (animator) -> mainExecutor.execute(() -> { animations.remove(animator); finishCb.run(); })); - va.setDuration(DEFAULT_APP_TRANSITION_DURATION); + va.setDuration(SIZE_CHANGE_ANIMATION_DURATION); + va.setInterpolator(Interpolators.EMPHASIZED); animations.add(va); } 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 07b385bdb045..69e1f36dec0b 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 @@ -1802,6 +1802,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mMultiInstanceHelper, mWindowDecorCaptionHandleRepository, mDesktopModeEventLogger, + mDesktopModeUiEventLogger, mDesktopModeCompatPolicy); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); 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 49c380a1a736..50bc7b5e865b 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 @@ -93,6 +93,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger; import com.android.wm.shell.desktopmode.DesktopModeUtils; import com.android.wm.shell.desktopmode.DesktopUserRepositories; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; @@ -211,6 +212,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final MultiInstanceHelper mMultiInstanceHelper; private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository; private final DesktopUserRepositories mDesktopUserRepositories; + private final DesktopModeUiEventLogger mDesktopModeUiEventLogger; private boolean mIsRecentsTransitionRunning = false; private boolean mIsDragging = false; private Runnable mLoadAppInfoRunnable; @@ -243,6 +245,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger, DesktopModeCompatPolicy desktopModeCompatPolicy) { this (context, userContext, displayController, taskResourceLoader, splitScreenController, desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler, @@ -257,7 +260,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper, windowDecorCaptionHandleRepository, desktopModeEventLogger, - desktopModeCompatPolicy); + desktopModeUiEventLogger, desktopModeCompatPolicy); } DesktopModeWindowDecoration( @@ -294,6 +297,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger, DesktopModeCompatPolicy desktopModeCompatPolicy) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, @@ -321,6 +325,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mTaskResourceLoader = taskResourceLoader; mTaskResourceLoader.onWindowDecorCreated(taskInfo); mDesktopModeCompatPolicy = desktopModeCompatPolicy; + mDesktopModeUiEventLogger = desktopModeUiEventLogger; } /** @@ -895,10 +900,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnCaptionTouchListener, mOnCaptionButtonClickListener, mWindowManagerWrapper, - mHandler + mHandler, + mDesktopModeUiEventLogger ); - } else if (mRelayoutParams.mLayoutResId - == R.layout.desktop_mode_app_header) { + } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) { return mAppHeaderViewHolderFactory.create( mResult.mRootView, mOnCaptionTouchListener, @@ -908,7 +913,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnLeftSnapClickListener, mOnRightSnapClickListener, mOnMaximizeOrRestoreClickListener, - mOnMaximizeHoverListener); + mOnMaximizeHoverListener, + mDesktopModeUiEventLogger); } throw new IllegalArgumentException("Unexpected layout resource id"); } @@ -1372,7 +1378,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer, mDisplayController, mTaskInfo, mContext, (width, height) -> calculateMaximizeMenuPosition(width, height), - mSurfaceControlTransactionSupplier); + mSurfaceControlTransactionSupplier, mDesktopModeUiEventLogger); mMaximizeMenu.show( /* isTaskInImmersiveMode= */ @@ -1488,6 +1494,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin shouldShowRestartButton, isBrowserApp, isBrowserApp ? getAppLink() : getBrowserLink(), + mDesktopModeUiEventLogger, mResult.mCaptionWidth, mResult.mCaptionHeight, mResult.mCaptionX, @@ -1973,6 +1980,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin MultiInstanceHelper multiInstanceHelper, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger, DesktopModeCompatPolicy desktopModeCompatPolicy) { return new DesktopModeWindowDecoration( context, @@ -2000,6 +2008,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin multiInstanceHelper, windowDecorCaptionHandleRepository, desktopModeEventLogger, + desktopModeUiEventLogger, desktopModeCompatPolicy); } } 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 f64b0d8695d2..25cf06b4247c 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 @@ -28,6 +28,7 @@ import android.graphics.Bitmap import android.graphics.Point import android.graphics.PointF import android.graphics.Rect +import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.MotionEvent.ACTION_OUTSIDE @@ -35,6 +36,7 @@ import android.view.SurfaceControl import android.view.View import android.view.WindowInsets.Type.systemBars import android.view.WindowManager +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.ImageButton import android.widget.ImageView import android.widget.Space @@ -49,6 +51,10 @@ import androidx.core.view.isGone import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.bubbles.ContextUtils.isRtl +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_DESKTOP_VIEW +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_FULLSCREEN +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_SPLIT_SCREEN import com.android.wm.shell.shared.annotations.ShellBackgroundThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper @@ -97,6 +103,7 @@ class HandleMenu( private val shouldShowRestartButton: Boolean, private val isBrowserApp: Boolean, private val openInAppOrBrowserIntent: Intent?, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, private val captionWidth: Int, private val captionHeight: Int, captionX: Int, @@ -208,6 +215,7 @@ class HandleMenu( ) { val handleMenuView = HandleMenuView( context = context, + desktopModeUiEventLogger = desktopModeUiEventLogger, menuWidth = menuWidth, captionHeight = captionHeight, shouldShowWindowingPill = shouldShowWindowingPill, @@ -475,6 +483,7 @@ class HandleMenu( @SuppressLint("ClickableViewAccessibility") class HandleMenuView( private val context: Context, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, menuWidth: Int, captionHeight: Int, private val shouldShowWindowingPill: Boolean, @@ -615,6 +624,45 @@ class HandleMenu( return@setOnTouchListener true } + desktopBtn.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun performAccessibilityAction( + host: View, + action: Int, + args: Bundle? + ): Boolean { + if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_DESKTOP_VIEW) + } + return super.performAccessibilityAction(host, action, args) + } + } + + fullscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun performAccessibilityAction( + host: View, + action: Int, + args: Bundle? + ): Boolean { + if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_FULLSCREEN) + } + return super.performAccessibilityAction(host, action, args) + } + } + + splitscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun performAccessibilityAction( + host: View, + action: Int, + args: Bundle? + ): Boolean { + if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_SPLIT_SCREEN) + } + return super.performAccessibilityAction(host, action, args) + } + } + with(context) { // Update a11y announcement out to say "double tap to enter Fullscreen" ViewCompat.replaceAccessibilityAction( @@ -917,6 +965,7 @@ interface HandleMenuFactory { shouldShowRestartButton: Boolean, isBrowserApp: Boolean, openInAppOrBrowserIntent: Intent?, + desktopModeUiEventLogger: DesktopModeUiEventLogger, captionWidth: Int, captionHeight: Int, captionX: Int, @@ -942,6 +991,7 @@ object DefaultHandleMenuFactory : HandleMenuFactory { shouldShowRestartButton: Boolean, isBrowserApp: Boolean, openInAppOrBrowserIntent: Intent?, + desktopModeUiEventLogger: DesktopModeUiEventLogger, captionWidth: Int, captionHeight: Int, captionX: Int, @@ -963,6 +1013,7 @@ object DefaultHandleMenuFactory : HandleMenuFactory { shouldShowRestartButton, isBrowserApp, openInAppOrBrowserIntent, + desktopModeUiEventLogger, captionWidth, captionHeight, captionX, 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 5d1a7a0cc3a2..a10826fca3e2 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 @@ -60,14 +60,16 @@ import android.window.TaskConstants import androidx.compose.material3.ColorScheme import androidx.compose.ui.graphics.toArgb import androidx.core.animation.addListener -import androidx.core.view.ViewCompat -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat import androidx.core.view.isGone import androidx.core.view.isVisible import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_MAXIMIZE +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_LEFT +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_RIGHT import com.android.wm.shell.desktopmode.isTaskMaximized import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN @@ -85,13 +87,14 @@ import java.util.function.Supplier * to the right or left half of the screen. */ class MaximizeMenu( - private val syncQueue: SyncTransactionQueue, - private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer, - private val displayController: DisplayController, - private val taskInfo: RunningTaskInfo, - private val decorWindowContext: Context, - private val positionSupplier: (Int, Int) -> Point, - private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() } + private val syncQueue: SyncTransactionQueue, + private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer, + private val displayController: DisplayController, + private val taskInfo: RunningTaskInfo, + private val decorWindowContext: Context, + private val positionSupplier: (Int, Int) -> Point, + private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger ) { private var maximizeMenu: AdditionalViewHostViewContainer? = null private var maximizeMenuView: MaximizeMenuView? = null @@ -132,7 +135,7 @@ class MaximizeMenu( onLeftSnapClickListener = onLeftSnapClickListener, onRightSnapClickListener = onRightSnapClickListener, onHoverListener = onHoverListener, - onOutsideTouchListener = onOutsideTouchListener + onOutsideTouchListener = onOutsideTouchListener, ) maximizeMenuView?.let { view -> view.animateOpenMenu(onEnd = { @@ -167,7 +170,7 @@ class MaximizeMenu( onLeftSnapClickListener: () -> Unit, onRightSnapClickListener: () -> Unit, onHoverListener: (Boolean) -> Unit, - onOutsideTouchListener: () -> Unit + onOutsideTouchListener: () -> Unit, ) { val t = transactionSupplier.get() val builder = SurfaceControl.Builder() @@ -186,6 +189,7 @@ class MaximizeMenu( "MaximizeMenu") maximizeMenuView = MaximizeMenuView( context = decorWindowContext, + desktopModeUiEventLogger = desktopModeUiEventLogger, sizeToggleDirection = getSizeToggleDirection(), immersiveConfig = if (showImmersiveOption) { MaximizeMenuView.ImmersiveConfig.Visible( @@ -265,6 +269,7 @@ class MaximizeMenu( */ class MaximizeMenuView( context: Context, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, private val sizeToggleDirection: SizeToggleDirection, immersiveConfig: ImmersiveConfig, showSnapOptions: Boolean, @@ -425,7 +430,10 @@ class MaximizeMenu( ) { super.onInitializeAccessibilityNodeInfo(host, info) - info.addAction(AccessibilityAction.ACTION_CLICK) + info.addAction(AccessibilityAction( + AccessibilityAction.ACTION_CLICK.id, + context.getString(R.string.maximize_menu_talkback_action_maximize_restore_text) + )) host.isClickable = true } @@ -435,6 +443,7 @@ class MaximizeMenu( args: Bundle? ): Boolean { if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_MAXIMIZE) onMaximizeClickListener?.invoke() } return super.performAccessibilityAction(host, action, args) @@ -447,7 +456,10 @@ class MaximizeMenu( info: AccessibilityNodeInfo ) { super.onInitializeAccessibilityNodeInfo(host, info) - info.addAction(AccessibilityAction.ACTION_CLICK) + info.addAction(AccessibilityAction( + AccessibilityAction.ACTION_CLICK.id, + context.getString(R.string.maximize_menu_talkback_action_snap_left_text) + )) host.isClickable = true } @@ -457,6 +469,7 @@ class MaximizeMenu( args: Bundle? ): Boolean { if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_LEFT) onLeftSnapClickListener?.invoke() } return super.performAccessibilityAction(host, action, args) @@ -469,7 +482,10 @@ class MaximizeMenu( info: AccessibilityNodeInfo ) { super.onInitializeAccessibilityNodeInfo(host, info) - info.addAction(AccessibilityAction.ACTION_CLICK) + info.addAction(AccessibilityAction( + AccessibilityAction.ACTION_CLICK.id, + context.getString(R.string.maximize_menu_talkback_action_snap_right_text) + )) host.isClickable = true } @@ -479,35 +495,13 @@ class MaximizeMenu( args: Bundle? ): Boolean { if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_RIGHT) onRightSnapClickListener?.invoke() } return super.performAccessibilityAction(host, action, args) } } - with(context.resources) { - ViewCompat.replaceAccessibilityAction( - snapLeftButton, - AccessibilityActionCompat.ACTION_CLICK, - getString(R.string.maximize_menu_talkback_action_snap_left_text), - null - ) - - ViewCompat.replaceAccessibilityAction( - snapRightButton, - AccessibilityActionCompat.ACTION_CLICK, - getString(R.string.maximize_menu_talkback_action_snap_right_text), - null - ) - - ViewCompat.replaceAccessibilityAction( - sizeToggleButton, - AccessibilityActionCompat.ACTION_CLICK, - getString(R.string.maximize_menu_talkback_action_maximize_restore_text), - null - ) - } - // Maximize/restore button. val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE) R.string.desktop_mode_maximize_menu_restore_button_text @@ -792,15 +786,15 @@ class MaximizeMenu( } /** Measure width of the root view of this menu. */ - fun measureWidth() : Int { - rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - return rootView.getMeasuredWidth() + fun measureWidth(): Int { + rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + return rootView.measuredWidth } /** Measure height of the root view of this menu. */ - fun measureHeight() : Int { - rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - return rootView.getMeasuredHeight() + fun measureHeight(): Int { + rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) + return rootView.measuredHeight } private fun deactivateSnapOptions() { @@ -1048,7 +1042,8 @@ interface MaximizeMenuFactory { taskInfo: RunningTaskInfo, decorWindowContext: Context, positionSupplier: (Int, Int) -> Point, - transactionSupplier: Supplier<Transaction> + transactionSupplier: Supplier<Transaction>, + desktopModeUiEventLogger: DesktopModeUiEventLogger, ): MaximizeMenu } @@ -1061,7 +1056,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory { taskInfo: RunningTaskInfo, decorWindowContext: Context, positionSupplier: (Int, Int) -> Point, - transactionSupplier: Supplier<Transaction> + transactionSupplier: Supplier<Transaction>, + desktopModeUiEventLogger: DesktopModeUiEventLogger, ): MaximizeMenu { return MaximizeMenu( syncQueue, @@ -1070,7 +1066,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory { taskInfo, decorWindowContext, positionSupplier, - transactionSupplier + transactionSupplier, + desktopModeUiEventLogger ) } } 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 c5057aa3cc18..f09f22fb1951 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 @@ -92,4 +92,7 @@ internal class DecorThemeUtil(private val context: Context) { Theme.LIGHT -> lightColors Theme.DARK -> darkColors } + + fun getColorScheme(isDarkMode: Boolean): ColorScheme = + if (isDarkMode) darkColors else lightColors } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt index 57f8046065b4..ab6b72947586 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt @@ -198,6 +198,11 @@ class DesktopTilingDividerWindowManager( tilingDividerView?.onUiModeChange(isDarkMode) } + /** Notifies the divider view of task info change and possible color change. */ + fun onTaskInfoChange() { + tilingDividerView?.onTaskInfoChange() + } + /** Hides the divider bar. */ fun hideDividerBar() { if (!dividerShown) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt index 854d9e1deef1..3e92d7d994a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt @@ -375,6 +375,7 @@ class DesktopTilingWindowDecoration( fun onTaskInfoChange(taskInfo: RunningTaskInfo) { val isCurrentTaskInDarkMode = isTaskInDarkMode(taskInfo) + desktopTilingDividerWindowManager?.onTaskInfoChange() if (isCurrentTaskInDarkMode == isDarkMode || !isTilingManagerInitialised) return isDarkMode = isCurrentTaskInDarkMode desktopTilingDividerWindowManager?.onUiModeChange(isDarkMode) @@ -432,15 +433,38 @@ class DesktopTilingWindowDecoration( startTransaction: Transaction, finishTransaction: Transaction, ) { + var leftTaskBroughtToFront = false + var rightTaskBroughtToFront = false + for (change in info.changes) { change.taskInfo?.let { if (it.isFullscreen || isMinimized(change.mode, info.type)) { removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen) } else if (isEnteringPip(change, info.type)) { removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen) + } else if (isTransitionToFront(change.mode, info.type)) { + handleTaskBroughtToFront(it.taskId) + leftTaskBroughtToFront = + leftTaskBroughtToFront || + it.taskId == leftTaskResizingHelper?.taskInfo?.taskId + rightTaskBroughtToFront = + rightTaskBroughtToFront || + it.taskId == rightTaskResizingHelper?.taskInfo?.taskId } } } + + if (leftTaskBroughtToFront && rightTaskBroughtToFront) { + desktopTilingDividerWindowManager?.showDividerBar() + } + } + + private fun handleTaskBroughtToFront(taskId: Int) { + if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) { + leftTaskResizingHelper?.onAppBecomingVisible() + } else if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) { + rightTaskResizingHelper?.onAppBecomingVisible() + } } private fun isMinimized(changeMode: Int, infoType: Int): Boolean { @@ -471,6 +495,9 @@ class DesktopTilingWindowDecoration( return false } + private fun isTransitionToFront(changeMode: Int, transitionType: Int): Boolean = + changeMode == TRANSIT_TO_FRONT && transitionType == TRANSIT_TO_FRONT + class AppResizingHelper( val taskInfo: RunningTaskInfo, val desktopModeWindowDecoration: DesktopModeWindowDecoration, @@ -484,6 +511,7 @@ class DesktopTilingWindowDecoration( ) { var isInitialised = false var newBounds = Rect(bounds) + var visibilityCallback: (() -> Unit)? = null private lateinit var resizeVeil: ResizeVeil private val displayContext = displayController.getDisplayContext(taskInfo.displayId) private val userContext = @@ -521,6 +549,11 @@ class DesktopTilingWindowDecoration( fun updateVeil(t: Transaction) = resizeVeil.updateTransactionWithResizeVeil(t, newBounds) + fun onAppBecomingVisible() { + visibilityCallback?.invoke() + visibilityCallback = null + } + fun hideVeil() = resizeVeil.hideVeil() private fun createIconFactory(context: Context, dimensions: Int): BaseIconFactory { @@ -593,11 +626,16 @@ class DesktopTilingWindowDecoration( removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate) leftTaskResizingHelper = null val taskId = rightTaskResizingHelper?.taskInfo?.taskId - if (taskId != null && taskRepository.isVisibleTask(taskId)) { + val callback: (() -> Unit)? = { rightTaskResizingHelper ?.desktopModeWindowDecoration ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate) } + if (taskId != null && taskRepository.isVisibleTask(taskId)) { + callback?.invoke() + } else if (rightTaskResizingHelper != null) { + rightTaskResizingHelper?.visibilityCallback = callback + } tearDownTiling() return } @@ -607,11 +645,17 @@ class DesktopTilingWindowDecoration( removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate) rightTaskResizingHelper = null val taskId = leftTaskResizingHelper?.taskInfo?.taskId - if (taskId != null && taskRepository.isVisibleTask(taskId)) { + val callback: (() -> Unit)? = { leftTaskResizingHelper ?.desktopModeWindowDecoration ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate) } + if (taskId != null && taskRepository.isVisibleTask(taskId)) { + callback?.invoke() + } else if (leftTaskResizingHelper != null) { + leftTaskResizingHelper?.visibilityCallback = callback + } + tearDownTiling() } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt index 54dcd2d082dc..e9bd6f8ef85c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt @@ -29,6 +29,7 @@ import android.view.RoundedCorner import android.view.View import android.view.ViewConfiguration import android.widget.FrameLayout +import androidx.compose.ui.graphics.toArgb import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags import com.android.wm.shell.R @@ -36,6 +37,7 @@ import com.android.wm.shell.common.split.DividerHandleView import com.android.wm.shell.common.split.DividerRoundedCorner import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.windowdecor.DragDetector +import com.android.wm.shell.windowdecor.common.DecorThemeUtil /** Divider for tiling split screen, currently mostly a copy of [DividerView]. */ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.MotionEventHandler { @@ -56,6 +58,9 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion @VisibleForTesting var handleY: IntRange = 0..0 private var canResize = false private var resized = false + private var isDarkMode = false + private var decorThemeUtil = DecorThemeUtil(context) + /** * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with * insets. @@ -90,10 +95,12 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion ) { callback = dividerMoveCallback this.dividerBounds.set(dividerBounds) + this.isDarkMode = isDarkMode + paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb() handle.setIsLeftRightSplit(true) handle.setup(/* isSplitScreen= */ false, isDarkMode) corners.setIsLeftRightSplit(true) - corners.setup(/* isSplitScreen= */ false, isDarkMode) + corners.setup(/* isSplitScreen= */ false, paint.color) handleRegionHeight = handleRegionSize.height handleRegionWidth = handleRegionSize.width cornersRadius = @@ -108,17 +115,22 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion } fun onUiModeChange(isDarkMode: Boolean) { + this.isDarkMode = isDarkMode handle.onUiModeChange(isDarkMode) - corners.onUiModeChange(isDarkMode) - paint.color = - if (isDarkMode) { - resources.getColor(R.color.tiling_divider_background_dark, null /* theme */) - } else { - resources.getColor(R.color.tiling_divider_background_light, null /* theme */) - } + paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb() + corners.onUiModeChange(paint.color) invalidate() } + fun onTaskInfoChange() { + decorThemeUtil = DecorThemeUtil(context) + if (paint.color != decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()) { + paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb() + corners.onCornerColorChange(paint.color) + invalidate() + } + } + override fun onFinishInflate() { super.onFinishInflate() dividerBar = requireViewById(R.id.divider_bar) @@ -128,15 +140,10 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion resources.getDimensionPixelSize(R.dimen.docked_stack_divider_lift_elevation) setOnTouchListener(this) setWillNotDraw(false) - paint.color = - if ( - context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - ) { - resources.getColor(R.color.tiling_divider_background_dark, /* theme= */null) - } else { - resources.getColor(R.color.tiling_divider_background_light, /* theme= */ null) - } + val isDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == + Configuration.UI_MODE_NIGHT_YES + paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb() paint.isAntiAlias = true paint.style = Paint.Style.FILL } 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 9d16be59ba34..f09c500dcdd0 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 @@ -39,6 +39,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit import com.android.internal.policy.SystemBarUtils import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_OPENED import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper import com.android.wm.shell.windowdecor.AppHandleAnimator import com.android.wm.shell.windowdecor.WindowManagerWrapper @@ -53,7 +55,8 @@ class AppHandleViewHolder( onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: OnClickListener, private val windowManagerWrapper: WindowManagerWrapper, - private val handler: Handler + private val handler: Handler, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, ) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) { data class HandleData( @@ -201,6 +204,7 @@ class AppHandleViewHolder( // Passthrough the a11y click action so the caption handle, so that app handle menu // is opened on a11y click, similar to a real click if (action == AccessibilityAction.ACTION_CLICK.id) { + desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_OPENED) captionHandle.performClick() } return super.performAccessibilityAction(host, action, args) @@ -216,10 +220,13 @@ class AppHandleViewHolder( } } - // Update a11y action text so that Talkback announces "Press double tap to open app handle - // menu" while focused on status bar input layer + // Update a11y action text so that Talkback announces "Press double tap to open menu" + // while focused on status bar input layer ViewCompat.replaceAccessibilityAction( - view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null + view, + AccessibilityActionCompat.ACTION_CLICK, + context.getString(R.string.app_handle_chip_accessibility_announce), + null ) } @@ -297,12 +304,14 @@ class AppHandleViewHolder( onCaptionButtonClickListener: OnClickListener, windowManagerWrapper: WindowManagerWrapper, handler: Handler, + desktopModeUiEventLogger: DesktopModeUiEventLogger, ): AppHandleViewHolder = AppHandleViewHolder( rootView, onCaptionTouchListener, onCaptionButtonClickListener, windowManagerWrapper, handler, + desktopModeUiEventLogger, ) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index 0e2698d0b6fa..d2e3a07f4651 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -49,6 +49,13 @@ import com.android.internal.R.color.materialColorSurfaceContainerHigh import com.android.internal.R.color.materialColorSurfaceContainerLow import com.android.internal.R.color.materialColorSurfaceDim import com.android.wm.shell.R +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_MAXIMIZE_RESTORE +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_LEFT +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_RIGHT +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_CLOSE_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MINIMIZE_BUTTON import com.android.wm.shell.windowdecor.MaximizeButtonView import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.DrawableInsets @@ -75,6 +82,7 @@ class AppHeaderViewHolder( mOnRightSnapClickListener: () -> Unit, mOnMaximizeOrRestoreClickListener: () -> Unit, onMaximizeHoverAnimationFinishedListener: () -> Unit, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, ) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) { data class HeaderData( @@ -151,6 +159,8 @@ class AppHeaderViewHolder( private lateinit var a11yTextMaximize: String private lateinit var a11yTextRestore: String + private lateinit var currentTaskInfo: RunningTaskInfo + init { captionView.setOnTouchListener(onCaptionTouchListener) captionHandle.setOnTouchListener(onCaptionTouchListener) @@ -197,9 +207,18 @@ class AppHeaderViewHolder( args: Bundle? ): Boolean { when (action) { - R.id.action_snap_left -> mOnLeftSnapClickListener.invoke() - R.id.action_snap_right -> mOnRightSnapClickListener.invoke() - R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke() + R.id.action_snap_left -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT) + mOnLeftSnapClickListener.invoke() + } + R.id.action_snap_right -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT) + mOnRightSnapClickListener.invoke() + } + R.id.action_maximize_restore -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE) + mOnMaximizeOrRestoreClickListener.invoke() + } } return super.performAccessibilityAction(host, action, args) @@ -224,10 +243,56 @@ class AppHeaderViewHolder( args: Bundle? ): Boolean { when (action) { - AccessibilityAction.ACTION_CLICK.id -> host.performClick() - R.id.action_snap_left -> mOnLeftSnapClickListener.invoke() - R.id.action_snap_right -> mOnRightSnapClickListener.invoke() - R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke() + AccessibilityAction.ACTION_CLICK.id -> { + desktopModeUiEventLogger.log( + currentTaskInfo, A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON + ) + host.performClick() + } + R.id.action_snap_left -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT) + mOnLeftSnapClickListener.invoke() + } + R.id.action_snap_right -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT) + mOnRightSnapClickListener.invoke() + } + R.id.action_maximize_restore -> { + desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE) + mOnMaximizeOrRestoreClickListener.invoke() + } + } + + return super.performAccessibilityAction(host, action, args) + } + } + + closeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun performAccessibilityAction( + host: View, + action: Int, + args: Bundle? + ): Boolean { + when (action) { + AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log( + currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON + ) + } + + return super.performAccessibilityAction(host, action, args) + } + } + + minimizeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun performAccessibilityAction( + host: View, + action: Int, + args: Bundle? + ): Boolean { + when (action) { + AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log( + currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON + ) } return super.performAccessibilityAction(host, action, args) @@ -310,7 +375,8 @@ class AppHeaderViewHolder( enableMaximizeLongClick: Boolean, isCaptionVisible: Boolean, ) { - if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) { + currentTaskInfo = taskInfo + if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue) { bindDataWithThemedHeaders( taskInfo, isTaskMaximized, @@ -361,7 +427,7 @@ class AppHeaderViewHolder( minimizeWindowButton.background = getDrawable(1) } maximizeButtonView.setAnimationTints(isDarkMode()) - minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue() + minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue } private fun bindDataWithThemedHeaders( @@ -417,7 +483,7 @@ class AppHeaderViewHolder( drawableInsets = minimizeDrawableInsets ) } - minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue() + minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue // Maximize button. maximizeButtonView.apply { setAnimationTints( @@ -761,6 +827,7 @@ class AppHeaderViewHolder( mOnRightSnapClickListener: () -> Unit, mOnMaximizeOrRestoreClickListener: () -> Unit, onMaximizeHoverAnimationFinishedListener: () -> Unit, + desktopModeUiEventLogger: DesktopModeUiEventLogger ): AppHeaderViewHolder = AppHeaderViewHolder( rootView, onCaptionTouchListener, @@ -771,6 +838,7 @@ class AppHeaderViewHolder( mOnRightSnapClickListener, mOnMaximizeOrRestoreClickListener, onMaximizeHoverAnimationFinishedListener, + desktopModeUiEventLogger, ) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java index 3d5e9495e29d..1e459d55ed77 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java @@ -18,6 +18,7 @@ package com.android.wm.shell.common; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -231,4 +232,24 @@ public class DisplayControllerTests extends ShellTestCase { assertEquals(DISPLAY_ABS_BOUNDS_1, mController.getDisplayLayout(DISPLAY_ID_1).globalBoundsDp()); } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG) + public void onDisplayConfigurationChanged_reInitDisplayLayout() + throws RemoteException { + ExtendedMockito.doReturn(true) + .when(() -> DesktopModeStatus.canEnterDesktopMode(any())); + mController.onInit(); + mController.addDisplayWindowListener(mListener); + + mCapturedTopologyListener.accept(mMockTopology); + + DisplayLayout displayLayoutBefore = mController.getDisplayLayout(DISPLAY_ID_0); + mDisplayContainerListener.onDisplayConfigurationChanged(DISPLAY_ID_0, new Configuration()); + DisplayLayout displayLayoutAfter = mController.getDisplayLayout(DISPLAY_ID_0); + + assertNotSame(displayLayoutBefore, displayLayoutAfter); + assertEquals(DISPLAY_ABS_BOUNDS_0, + mController.getDisplayLayout(DISPLAY_ID_0).globalBoundsDp()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index 3fb008377d6e..6ede990df15e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -1280,6 +1280,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { @Test @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun removeDesk_multipleDesks_active_marksInactiveInDisplay() { + repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2) + repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3) + repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3) + + repo.removeDesk(deskId = 3) + + assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isNull() + } + + @Test + @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun removeDesk_multipleDesks_inactive_removes() { repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2) repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3) @@ -1292,6 +1304,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() { } @Test + @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun removeDesk_multipleDesks_inactive_keepsOtherDeskActiveInDisplay() { + repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2) + repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3) + repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3) + + repo.removeDesk(deskId = 2) + + assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isEqualTo(3) + } + + @Test @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE) fun removeDesk_removesFromPersistence() = runTest(StandardTestDispatcher()) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index ed69d912c4ef..2126d1d9b986 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -337,7 +337,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { mockDesktopModeWindowDecorFactory.create( any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any()) + any(), any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.user).thenReturn(mockUserHandle) 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 77513adf0088..0908f56e1cfb 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 @@ -106,6 +106,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.DesktopUserRepositories; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; @@ -246,6 +247,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private DesktopModeEventLogger mDesktopModeEventLogger; @Mock + private DesktopModeUiEventLogger mDesktopModeUiEventLogger; + @Mock private DesktopRepository mDesktopRepository; @Mock private WindowDecorTaskResourceLoader mMockTaskResourceLoader; @@ -303,15 +306,15 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyInt(), anyInt(), anyInt(), anyInt())) + anyBoolean(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt())) .thenReturn(mMockHandleMenu); when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt())) .thenReturn(false); when(mMockAppHeaderViewHolderFactory - .create(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(mMockAppHeaderViewHolder); when(mMockAppHandleViewHolderFactory - .create(any(), any(), any(), any(), any())) + .create(any(), any(), any(), any(), any(), any())) .thenReturn(mMockAppHandleViewHolder); when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository); when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository); @@ -1797,7 +1800,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), argThat(intent -> (uri == null && intent == null) || intent.getData().equals(uri)), - anyInt(), anyInt(), anyInt(), anyInt()); + any(), anyInt(), anyInt(), anyInt(), anyInt()); } private void createMaximizeMenu(DesktopModeWindowDecoration decoration) { @@ -1894,7 +1897,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger, - mDesktopModeCompatPolicy); + mDesktopModeUiEventLogger, mDesktopModeCompatPolicy); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); @@ -2020,7 +2023,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @NonNull Context decorWindowContext, @NonNull Function2<? super Integer,? super Integer,? extends Point> positionSupplier, - @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) { + @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier, + @NonNull DesktopModeUiEventLogger desktopModeUiEventLogger) { return mMaximizeMenu; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt index e4b897264883..b0b95c97ae18 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt @@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger 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 @@ -95,6 +96,8 @@ class HandleMenuTest : ShellTestCase() { private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader @Mock private lateinit var mockAppIcon: Bitmap + @Mock + private lateinit var mockDesktopModeUiEventLogger: DesktopModeUiEventLogger private lateinit var handleMenu: HandleMenu @@ -290,6 +293,7 @@ class HandleMenuTest : ShellTestCase() { shouldShowRestartButton = true, isBrowserApp = false, null /* openInAppOrBrowserIntent */, + mockDesktopModeUiEventLogger, captionWidth = HANDLE_WIDTH, captionHeight = 50, captionX = captionX, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt index e5f8d7d34a47..0adca5e864bf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt @@ -587,6 +587,31 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { } @Test + fun tilingDivider_shouldBeShown_whenTiledTasksBecomeVisible() { + val task1 = createVisibleTask() + val task2 = createVisibleTask() + val additionalTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock() + whenever(tiledTaskHelper.taskInfo).thenReturn(task1) + whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration) + whenever(additionalTaskHelper.taskInfo).thenReturn(task2) + whenever(additionalTaskHelper.desktopModeWindowDecoration) + .thenReturn(desktopWindowDecoration) + + tilingDecoration.leftTaskResizingHelper = tiledTaskHelper + tilingDecoration.rightTaskResizingHelper = additionalTaskHelper + tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager + val changeInfo = createTransitFrontTransition(task1, task2) + tilingDecoration.onTransitionReady( + transition = mock(), + info = changeInfo, + startTransaction = mock(), + finishTransaction = mock(), + ) + + verify(desktopTilingDividerWindowManager, times(1)).showDividerBar() + } + + @Test fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() { val task1 = createVisibleTask() val task2 = createVisibleTask() @@ -762,6 +787,30 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { ) } + private fun createTransitFrontTransition( + task1: RunningTaskInfo?, + task2: RunningTaskInfo?, + type: Int = TRANSIT_TO_FRONT, + ) = + TransitionInfo(type, /* flags= */ 0).apply { + addChange( + Change(mock(), mock()).apply { + mode = TRANSIT_TO_FRONT + parent = null + taskInfo = task1 + flags = flags + } + ) + addChange( + Change(mock(), mock()).apply { + mode = TRANSIT_TO_FRONT + parent = null + taskInfo = task2 + flags = flags + } + ) + } + companion object { private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100) private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt index 2c3009cb8dc4..e23019a59307 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt @@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest import com.android.internal.policy.SystemBarUtils import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger import com.android.wm.shell.windowdecor.WindowManagerWrapper import org.junit.Before import org.junit.runner.RunWith @@ -57,6 +58,7 @@ class AppHandleViewHolderTest : ShellTestCase() { private val mockWindowManagerWrapper = mock<WindowManagerWrapper>() private val mockHandler = mock<Handler>() private val mockTaskInfo = mock<RunningTaskInfo>() + private val mockDesktopModeUiEventLogger = mock<DesktopModeUiEventLogger>() @Before fun setup() { @@ -92,7 +94,8 @@ class AppHandleViewHolderTest : ShellTestCase() { mockOnTouchListener, mockOnClickListener, mockWindowManagerWrapper, - mockHandler + mockHandler, + mockDesktopModeUiEventLogger, ) } } diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 104ece6582f5..b6a2ad7064a9 100644 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -28,7 +28,9 @@ #include "SkRefCnt.h" #include "SkStream.h" #include "SkTypes.h" +#ifdef __linux__ // Only Linux support parcel #include "android/binder_parcel.h" +#endif #include "android_nio_utils.h" #ifdef __ANDROID__ @@ -851,6 +853,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { #endif } +#ifdef __linux__ // Only Linux support parcel // Returns whether this bitmap should be written to the parcel as mutable. static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) { // If the bitmap is immutable, then parcel as immutable. @@ -867,6 +870,7 @@ static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) { // writing it to the parcel. return !shouldUseAshmem(parcel, bitmap.computeByteSize()); } +#endif static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density, jobject parcel) { diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp index 476b6fda5007..6c82aa1ca27d 100644 --- a/libs/hwui/jni/GIFMovie.cpp +++ b/libs/hwui/jni/GIFMovie.cpp @@ -63,7 +63,7 @@ GIFMovie::GIFMovie(SkStream* stream) } fCurrIndex = -1; fLastDrawIndex = -1; - fPaintingColor = SK_AlphaTRANSPARENT; + fPaintingColor = SkPackARGB32(0, 0, 0, 0); } GIFMovie::~GIFMovie() @@ -127,7 +127,7 @@ static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObje for (; width > 0; width--, src++, dst++) { if (*src != transparent && *src < cmap->ColorCount) { const GifColorType& col = cmap->Colors[*src]; - *dst = SkColorSetRGB(col.Red, col.Green, col.Blue); + *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue); } } } @@ -395,10 +395,10 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm) lastIndex = fGIF->ImageCount - 1; } - SkColor bgColor = SK_ColorTRANSPARENT; + SkColor bgColor = SkPackARGB32(0, 0, 0, 0); if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) { const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor]; - bgColor = SkColorSetRGB(col.Red, col.Green, col.Blue); + bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue); } // draw each frames - not intelligent way @@ -411,7 +411,7 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm) if (!trans && gif->SColorMap != nullptr) { fPaintingColor = bgColor; } else { - fPaintingColor = SK_ColorTRANSPARENT; + fPaintingColor = SkColorSetARGB(0, 0, 0, 0); } bm->eraseColor(fPaintingColor); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 0cd9c53c830f..e5a6260cfd98 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -44,7 +44,7 @@ namespace uirenderer { namespace renderthread { // Not all of these are strictly required, but are all enabled if present. -static std::array<std::string_view, 23> sEnableExtensions{ +static std::array<std::string_view, 25> sEnableExtensions{ VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, @@ -68,6 +68,8 @@ static std::array<std::string_view, 23> sEnableExtensions{ VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME, VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME, VK_EXT_DEVICE_FAULT_EXTENSION_NAME, + VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME, + VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME, }; static bool shouldEnableExtension(const std::string_view& extension) { @@ -746,7 +748,14 @@ VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) { ALOGE_IF(!context, "Surface is not backed by gpu"); GrSemaphoresSubmitted submitted = context->flush( surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo); - context->submit(); + + static uint64_t currentFrameID = 0; + GrSubmitInfo submitInfo; + if (!mFrameBoundaryANDROID) { + submitInfo.fMarkBoundary = GrMarkFrameBoundary::kYes; + submitInfo.fFrameID = currentFrameID++; + } + context->submit(submitInfo); VkDrawResult drawResult{ .submissionTime = systemTime(), }; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index a593ec6f8351..744211e39f79 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -30,14 +30,10 @@ // VK_ANDROID_frame_boundary is a bespoke extension defined by AGI // (https://github.com/google/agi) to enable profiling of apps rendering via // HWUI. This extension is not defined in Khronos, hence the need to declare it -// manually here. There's a superseding extension (VK_EXT_frame_boundary) being -// discussed in Khronos, but in the meantime we use the bespoke -// VK_ANDROID_frame_boundary. This is a device extension that is implemented by +// manually here. There's an extension (VK_EXT_frame_boundary) which we will use +// instead if available. This is a device extension that is implemented by // AGI's Vulkan capture layer, such that it is only supported by devices when // AGI is doing a capture of the app. -// -// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has -// landed in the spec. typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore, VkImage image); #define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary" diff --git a/location/api/system-current.txt b/location/api/system-current.txt index eb19ba84ee62..47984745fafc 100644 --- a/location/api/system-current.txt +++ b/location/api/system-current.txt @@ -96,8 +96,8 @@ package android.location { public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime implements android.os.Parcelable { method public int describeContents(); + method @IntRange(from=0, to=31) public int getAode(); method @IntRange(from=0) public int getBeidouWeekNumber(); - method @IntRange(from=0, to=31) public int getIode(); method @IntRange(from=0, to=604792) public int getToeSeconds(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime> CREATOR; @@ -106,8 +106,8 @@ package android.location { public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder { ctor public BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder(); method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime build(); + method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setAode(int); method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setBeidouWeekNumber(@IntRange(from=0) int); - method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setIode(int); method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604792) int); } @@ -177,7 +177,7 @@ package android.location { method public int describeContents(); method @Nullable public android.location.GnssAlmanac getAlmanac(); method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation(); - method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel(); + method @Nullable public android.location.GalileoIonosphericModel getIonosphericModel(); method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel(); method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels(); method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections(); @@ -193,7 +193,7 @@ package android.location { method @NonNull public android.location.GalileoAssistance build(); method @NonNull public android.location.GalileoAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac); method @NonNull public android.location.GalileoAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation); - method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel); + method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.GalileoIonosphericModel); method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel); method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>); method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>); diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java index 3382c20964d9..dc5e8b89d7d7 100644 --- a/location/java/android/location/BeidouSatelliteEphemeris.java +++ b/location/java/android/location/BeidouSatelliteEphemeris.java @@ -527,7 +527,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable { * * <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.11 Table 5-8. */ - private final int mIode; + private final int mAode; /** Beidou week number without rollover */ private final int mBeidouWeekNumber; @@ -540,18 +540,18 @@ public final class BeidouSatelliteEphemeris implements Parcelable { private final int mToeSeconds; private BeidouSatelliteEphemerisTime(Builder builder) { - Preconditions.checkArgumentInRange(builder.mIode, 0, 31, "Iode"); + Preconditions.checkArgumentInRange(builder.mAode, 0, 31, "Aode"); Preconditions.checkArgument(builder.mBeidouWeekNumber >= 0); Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604792, "ToeSeconds"); - mIode = builder.mIode; + mAode = builder.mAode; mBeidouWeekNumber = builder.mBeidouWeekNumber; mToeSeconds = builder.mToeSeconds; } /** Returns the AODE Age of Data, Ephemeris. */ @IntRange(from = 0, to = 31) - public int getIode() { - return mIode; + public int getAode() { + return mAode; } /** Returns the Beidou week number without rollover . */ @@ -573,7 +573,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable { public BeidouSatelliteEphemerisTime createFromParcel(Parcel in) { final BeidouSatelliteEphemerisTime.Builder beidouSatelliteEphemerisTime = new Builder() - .setIode(in.readInt()) + .setAode(in.readInt()) .setBeidouWeekNumber(in.readInt()) .setToeSeconds(in.readInt()); return beidouSatelliteEphemerisTime.build(); @@ -592,7 +592,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable { @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { - parcel.writeInt(mIode); + parcel.writeInt(mAode); parcel.writeInt(mBeidouWeekNumber); parcel.writeInt(mToeSeconds); } @@ -600,7 +600,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable { @Override public String toString() { StringBuilder builder = new StringBuilder("BeidouSatelliteEphemerisTime["); - builder.append("iode = ").append(mIode); + builder.append("aode = ").append(mAode); builder.append(", beidouWeekNumber = ").append(mBeidouWeekNumber); builder.append(", toeSeconds = ").append(mToeSeconds); builder.append("]"); @@ -609,14 +609,14 @@ public final class BeidouSatelliteEphemeris implements Parcelable { /** Builder for {@link BeidouSatelliteEphemerisTime} */ public static final class Builder { - private int mIode; + private int mAode; private int mBeidouWeekNumber; private int mToeSeconds; /** Sets the AODE Age of Data, Ephemeris. */ @NonNull - public Builder setIode(int iode) { - mIode = iode; + public Builder setAode(int iode) { + mAode = iode; return this; } diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java index 8a09e6634d09..7f81ccdf346f 100644 --- a/location/java/android/location/GalileoAssistance.java +++ b/location/java/android/location/GalileoAssistance.java @@ -41,8 +41,8 @@ public final class GalileoAssistance implements Parcelable { /** The Galileo almanac. */ @Nullable private final GnssAlmanac mAlmanac; - /** The Klobuchar ionospheric model. */ - @Nullable private final KlobucharIonosphericModel mIonosphericModel; + /** The Galileo ionospheric model. */ + @Nullable private final GalileoIonosphericModel mIonosphericModel; /** The UTC model. */ @Nullable private final UtcModel mUtcModel; @@ -102,9 +102,9 @@ public final class GalileoAssistance implements Parcelable { return mAlmanac; } - /** Returns the Klobuchar ionospheric model. */ + /** Returns the Galileo ionospheric model. */ @Nullable - public KlobucharIonosphericModel getIonosphericModel() { + public GalileoIonosphericModel getIonosphericModel() { return mIonosphericModel; } @@ -192,7 +192,7 @@ public final class GalileoAssistance implements Parcelable { return new GalileoAssistance.Builder() .setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR)) .setIonosphericModel( - in.readTypedObject(KlobucharIonosphericModel.CREATOR)) + in.readTypedObject(GalileoIonosphericModel.CREATOR)) .setUtcModel(in.readTypedObject(UtcModel.CREATOR)) .setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR)) .setAuxiliaryInformation( @@ -216,7 +216,7 @@ public final class GalileoAssistance implements Parcelable { /** Builder for {@link GalileoAssistance}. */ public static final class Builder { private GnssAlmanac mAlmanac; - private KlobucharIonosphericModel mIonosphericModel; + private GalileoIonosphericModel mIonosphericModel; private UtcModel mUtcModel; private LeapSecondsModel mLeapSecondsModel; private AuxiliaryInformation mAuxiliaryInformation; @@ -232,9 +232,9 @@ public final class GalileoAssistance implements Parcelable { return this; } - /** Sets the Klobuchar ionospheric model. */ + /** Sets the Galileo ionospheric model. */ @NonNull - public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) { + public Builder setIonosphericModel(@Nullable GalileoIonosphericModel ionosphericModel) { mIonosphericModel = ionosphericModel; return this; } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index c505bcee0332..b97b943113b6 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -447,10 +447,8 @@ interface IAudioService { boolean isAudioServerRunning(); - @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher avc); - @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") oneway void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher avc); int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes, diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING index b33097c50002..ea62287b7411 100644 --- a/media/java/android/media/projection/TEST_MAPPING +++ b/media/java/android/media/projection/TEST_MAPPING @@ -1,7 +1,7 @@ { - "presubmit": [ + "imports": [ { - "name": "MediaProjectionTests" + "path": "frameworks/base/services/core/java/com/android/server/media/projection" } ] -} +}
\ No newline at end of file diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml index 466da7e66fbf..5c911b135a5d 100644 --- a/media/tests/AudioPolicyTest/AndroidManifest.xml +++ b/media/tests/AudioPolicyTest/AndroidManifest.xml @@ -19,7 +19,6 @@ <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> - <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" /> diff --git a/media/tests/projection/TEST_MAPPING b/media/tests/projection/TEST_MAPPING new file mode 100644 index 000000000000..b33097c50002 --- /dev/null +++ b/media/tests/projection/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "MediaProjectionTests" + } + ] +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java index bcc737a351a9..d05aaaa6e389 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java @@ -52,8 +52,11 @@ import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Stream; /** * This is a utility class for defining some utility methods and constants @@ -69,7 +72,8 @@ public class PackageUtil { //intent attribute strings related to uninstall public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName"; private static final String DOWNLOADS_AUTHORITY = "downloads"; - private static final String SPLIT_BASE_APK_END_WITH = "base.apk"; + private static final String SPLIT_BASE_APK_SUFFIX = "base.apk"; + private static final String SPLIT_APK_SUFFIX = ".apk"; /** * Utility method to get package information for a given {@link File} @@ -77,11 +81,20 @@ public class PackageUtil { @Nullable public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) { String filePath = sourceFile.getAbsolutePath(); - if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) { + if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) { File dir = sourceFile.getParentFile(); - if (dir.listFiles().length > 1) { - // split apks, use file directory to get archive info - filePath = dir.getPath(); + try (Stream<Path> list = Files.list(dir.toPath())) { + long count = list + .filter((name) -> name.endsWith(SPLIT_APK_SUFFIX)) + .limit(2) + .count(); + if (count > 1) { + // split apks, use file directory to get archive info + filePath = dir.getPath(); + } + } catch (Exception ignored) { + // No access to the parent directory, proceed to read app snippet + // from the base apk only } } try { @@ -240,9 +253,10 @@ public class PackageUtil { appInfo.publicSourceDir = archiveFilePath; if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) { - final File[] files = sourceFile.getParentFile().listFiles(); + final File[] files = sourceFile.getParentFile().listFiles( + (dir, name) -> name.endsWith(SPLIT_APK_SUFFIX)); final String[] splits = Arrays.stream(appInfo.splitNames) - .map(i -> findFilePath(files, i + ".apk")) + .map(i -> findFilePath(files, i + SPLIT_APK_SUFFIX)) .filter(Objects::nonNull) .toArray(String[]::new); @@ -283,7 +297,9 @@ public class PackageUtil { } private static String findFilePath(File[] files, String postfix) { - for (File file : files) { + final int length = files != null ? files.length : 0; + for (int i = 0; i < length; i++) { + File file = files[i]; final String path = file.getAbsolutePath(); if (path.endsWith(postfix)) { return path; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt index e8477ef261a8..b84b903ac1cb 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt +++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt @@ -41,6 +41,8 @@ import android.util.Log import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet import java.io.ByteArrayOutputStream import java.io.File +import java.nio.file.Files +import java.nio.file.Path import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize @@ -48,6 +50,7 @@ object PackageUtil { private val LOG_TAG = InstallRepository::class.java.simpleName private const val DOWNLOADS_AUTHORITY = "downloads" private const val SPLIT_BASE_APK_SUFFIX = "base.apk" + private const val SPLIT_APK_SUFFIX = ".apk" const val localLogv = false const val ARGS_ABORT_REASON: String = "abort_reason" @@ -440,9 +443,20 @@ object PackageUtil { var filePath = sourceFile.absolutePath if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) { val dir = sourceFile.parentFile - if ((dir?.listFiles()?.size ?: 0) > 1) { - // split apks, use file directory to get archive info - filePath = dir.path + try { + Files.list(dir.toPath()).use { list -> + val count: Long = list + .filter { name: Path -> name.endsWith(SPLIT_APK_SUFFIX) } + .limit(2) + .count() + if (count > 1) { + // split apks, use file directory to get archive info + filePath = dir.path + } + } + } catch (ignored: Exception) { + // No access to the parent directory, proceed to read app snippet + // from the base apk only } } return try { diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java index 8aee576c3d04..a06d8829e331 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java @@ -24,11 +24,11 @@ import android.provider.Settings; * Implementation of {@link SecureSettings} that uses Android's {@link Settings.Secure} * implementation. */ -class AndroidSecureSettings implements SecureSettings { +public class AndroidSecureSettings implements SecureSettings { private final ContentResolver mContentResolver; - AndroidSecureSettings(ContentResolver contentResolver) { + public AndroidSecureSettings(ContentResolver contentResolver) { mContentResolver = contentResolver; } diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt index fdde3d3f5669..1da17756fae6 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt @@ -17,6 +17,7 @@ package com.android.settingslib.devicestate import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK +import android.util.Dumpable /** * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting. @@ -25,7 +26,7 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK * specific device states, retrieve the setting value, and check if rotation is locked for specific * or all device states. */ -interface DeviceStateAutoRotateSettingManager { +interface DeviceStateAutoRotateSettingManager : Dumpable { // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis. /** Listener for changes in device-state based auto rotate setting. */ @@ -65,5 +66,3 @@ data class SettableDeviceState( /** Returns whether there is an auto-rotation setting for this device state. */ val isSettable: Boolean ) - - diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt index 0b6c6e238956..a9f9eda07118 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt @@ -22,11 +22,13 @@ import android.os.UserHandle import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED +import android.util.IndentingPrintWriter import android.util.Log import android.util.SparseIntArray import com.android.internal.R import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener import com.android.window.flags.Flags +import java.io.PrintWriter import java.util.concurrent.Executor /** @@ -104,6 +106,15 @@ class DeviceStateAutoRotateSettingManagerImpl( throw UnsupportedOperationException("API updateSetting is not implemented yet") } + override fun dump(writer: PrintWriter, args: Array<out String>?) { + val indentingWriter = IndentingPrintWriter(writer) + indentingWriter.println("DeviceStateAutoRotateSettingManagerImpl") + indentingWriter.increaseIndent() + indentingWriter.println("fallbackPostureMap: $fallbackPostureMap") + indentingWriter.println("settableDeviceState: $settableDeviceState") + indentingWriter.decreaseIndent() + } + private fun notifyListeners() = settingListeners.forEach { listener -> listener.onSettingsChanged() } diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt new file mode 100644 index 000000000000..2db8e6f97498 --- /dev/null +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt @@ -0,0 +1,53 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.devicestate + +import android.content.Context +import android.os.Handler +import com.android.window.flags.Flags +import java.util.concurrent.Executor + +/** + * Provides appropriate instance of [DeviceStateAutoRotateSettingManager], based on the value of + * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. + */ +object DeviceStateAutoRotateSettingManagerProvider { + /** + * Provides an instance of [DeviceStateAutoRotateSettingManager], based on the value of + * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. It is supposed to be used + * by apps that supports dagger. + */ + @JvmStatic + fun createInstance( + context: Context, + backgroundExecutor: Executor, + secureSettings: SecureSettings, + mainHandler: Handler, + posturesHelper: PosturesHelper, + ): DeviceStateAutoRotateSettingManager = + if (Flags.enableDeviceStateAutoRotateSettingRefactor()) { + DeviceStateAutoRotateSettingManagerImpl( + context, + backgroundExecutor, + secureSettings, + mainHandler, + posturesHelper, + ) + } else { + DeviceStateRotationLockSettingsManager(context, secureSettings) + } +} diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java index deeba574f2ad..6d180b63cd08 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java @@ -20,10 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED; import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED; -import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener; - +import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; @@ -41,6 +39,7 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -51,7 +50,8 @@ import java.util.Set; * Manages device-state based rotation lock settings. Handles reading, writing, and listening for * changes. */ -public final class DeviceStateRotationLockSettingsManager { +public final class DeviceStateRotationLockSettingsManager implements + DeviceStateAutoRotateSettingManager { private static final String TAG = "DSRotLockSettingsMngr"; private static final String SEPARATOR_REGEX = ":"; @@ -68,8 +68,7 @@ public final class DeviceStateRotationLockSettingsManager { private SparseIntArray mPostureRotationLockFallbackSettings; private List<SettableDeviceState> mSettableDeviceStates; - @VisibleForTesting - DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { + public DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { mSecureSettings = secureSettings; mPosturesHelper = new PosturesHelper(context, getDeviceStateManager(context)); @@ -89,30 +88,6 @@ public final class DeviceStateRotationLockSettingsManager { return null; } - /** Returns a singleton instance of this class */ - public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) { - if (sSingleton == null) { - Context applicationContext = context.getApplicationContext(); - ContentResolver contentResolver = applicationContext.getContentResolver(); - SecureSettings secureSettings = new AndroidSecureSettings(contentResolver); - sSingleton = - new DeviceStateRotationLockSettingsManager(applicationContext, secureSettings); - } - return sSingleton; - } - - /** Resets the singleton instance of this class. Only used for testing. */ - @VisibleForTesting - public static synchronized void resetInstance() { - sSingleton = null; - } - - /** Returns true if device-state based rotation lock settings are enabled. */ - public static boolean isDeviceStateRotationLockEnabled(Context context) { - return context.getResources() - .getStringArray(R.array.config_perDeviceStateRotationLockDefaults).length > 0; - } - private void listenForSettingsChange() { mSecureSettings .registerContentObserver( @@ -131,7 +106,8 @@ public final class DeviceStateRotationLockSettingsManager { * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings * change. Can be called multiple times with different listeners. */ - public void registerListener(DeviceStateAutoRotateSettingListener runnable) { + @Override + public void registerListener(@NonNull DeviceStateAutoRotateSettingListener runnable) { mListeners.add(runnable); } @@ -139,14 +115,16 @@ public final class DeviceStateRotationLockSettingsManager { * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance * was never registered. */ + @Override public void unregisterListener( - DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) { + @NonNull DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) { if (!mListeners.remove(deviceStateAutoRotateSettingListener)) { Log.w(TAG, "Attempting to unregister a listener hadn't been registered"); } } /** Updates the rotation lock setting for a specified device state. */ + @Override public void updateSetting(int deviceState, boolean rotationLocked) { int posture = mPosturesHelper.deviceStateToPosture(deviceState); if (mPostureRotationLockFallbackSettings.indexOfKey(posture) >= 0) { @@ -173,6 +151,7 @@ public final class DeviceStateRotationLockSettingsManager { * DEVICE_STATE_ROTATION_LOCK_IGNORED}. */ @Settings.Secure.DeviceStateRotationLockSetting + @Override public int getRotationLockSetting(int deviceState) { int devicePosture = mPosturesHelper.deviceStateToPosture(deviceState); int rotationLockSetting = mPostureRotationLockSettings.get( @@ -196,6 +175,7 @@ public final class DeviceStateRotationLockSettingsManager { /** Returns true if the rotation is locked for the current device state */ + @Override public boolean isRotationLocked(int deviceState) { return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED; } @@ -204,6 +184,7 @@ public final class DeviceStateRotationLockSettingsManager { * Returns true if there is no device state for which the current setting is {@link * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}. */ + @Override public boolean isRotationLockedForAllStates() { for (int i = 0; i < mPostureRotationLockSettings.size(); i++) { if (mPostureRotationLockSettings.valueAt(i) @@ -215,6 +196,8 @@ public final class DeviceStateRotationLockSettingsManager { } /** Returns a list of device states and their respective auto-rotation setting availability. */ + @Override + @NonNull public List<SettableDeviceState> getSettableDeviceStates() { // Returning a copy to make sure that nothing outside can mutate our internal list. return new ArrayList<>(mSettableDeviceStates); @@ -356,17 +339,21 @@ public final class DeviceStateRotationLockSettingsManager { } } - /** Dumps internal state. */ - public void dump(IndentingPrintWriter pw) { - pw.println("DeviceStateRotationLockSettingsManager"); - pw.increaseIndent(); - pw.println("mPostureRotationLockDefaults: " + @Override + public void dump(@NonNull PrintWriter writer, String[] args) { + IndentingPrintWriter indentingWriter = new IndentingPrintWriter(writer); + indentingWriter.println("DeviceStateRotationLockSettingsManager"); + indentingWriter.increaseIndent(); + indentingWriter.println("mPostureRotationLockDefaults: " + Arrays.toString(mPostureRotationLockDefaults)); - pw.println("mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings); - pw.println("mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings); - pw.println("mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings); - pw.println("mSettableDeviceStates: " + mSettableDeviceStates); - pw.decreaseIndent(); + indentingWriter.println( + "mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings); + indentingWriter.println( + "mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings); + indentingWriter.println( + "mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings); + indentingWriter.println("mSettableDeviceStates: " + mSettableDeviceStates); + indentingWriter.decreaseIndent(); } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java index 155c7e6530aa..e46574cef917 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java @@ -37,9 +37,11 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.android.settingslib.R; +import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; public class LeAudioProfile implements LocalBluetoothProfile { @@ -59,6 +61,10 @@ public class LeAudioProfile implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 1; + // Cached callbacks being registered before service is connected. + private ConcurrentHashMap<BluetoothLeAudio.Callback, Executor> + mCachedCallbackExecutorMap = new ConcurrentHashMap<>(); + // These callbacks run on the main thread. private final class LeAudioServiceListener implements BluetoothProfile.ServiceListener { @@ -88,7 +94,19 @@ public class LeAudioProfile implements LocalBluetoothProfile { // Check current list of CachedDevices to see if any are hearing aid devices. mDeviceManager.updateHearingAidsDevices(); mProfileManager.callServiceConnectedListeners(); - mIsProfileReady = true; + if (!mIsProfileReady) { + mIsProfileReady = true; + if (Flags.adoptPrimaryGroupManagementApiV2()) { + if (DEBUG) { + Log.d( + TAG, + "onServiceConnected, register mCachedCallbackExecutorMap = " + + mCachedCallbackExecutorMap); + } + mCachedCallbackExecutorMap.forEach( + (callback, executor) -> registerCallback(executor, callback)); + } + } } public void onServiceDisconnected(int profile) { @@ -96,7 +114,12 @@ public class LeAudioProfile implements LocalBluetoothProfile { Log.d(TAG, "Bluetooth service disconnected"); } mProfileManager.callServiceDisconnectedListeners(); - mIsProfileReady = false; + if (mIsProfileReady) { + mIsProfileReady = false; + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.clear(); + } + } } } @@ -367,6 +390,9 @@ public class LeAudioProfile implements LocalBluetoothProfile { @NonNull BluetoothLeAudio.Callback callback) { if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot register callback."); + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.putIfAbsent(callback, executor); + } return; } mService.registerCallback(executor, callback); @@ -384,6 +410,9 @@ public class LeAudioProfile implements LocalBluetoothProfile { * callback is registered */ public void unregisterCallback(@NonNull BluetoothLeAudio.Callback callback) { + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.remove(callback); + } if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot unregister callback."); return; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt index 78dba57028ba..a9329ba3c76c 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt @@ -181,8 +181,10 @@ class DeviceStateAutoRotateSettingManagerImplTest { @Test fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() { - persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED) - persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED) + persistSettings( + "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" + + "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED" + ) val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED) @@ -276,7 +278,6 @@ class DeviceStateAutoRotateSettingManagerImplTest { SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true), SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false), SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false), - SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false), ) } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt new file mode 100644 index 000000000000..c3ec4edfdee5 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt @@ -0,0 +1,89 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.devicestate + +import android.content.Context +import android.os.Handler +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import java.util.concurrent.Executor +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceStateAutoRotateSettingManagerProviderTest { + + @get:Rule + val setFlagsRule: SetFlagsRule = SetFlagsRule() + + @get:Rule val rule = MockitoJUnit.rule() + + private val context: Context = ApplicationProvider.getApplicationContext() + + @Mock + private lateinit var mockExecutor: Executor + + @Mock + private lateinit var mockSecureSettings: SecureSettings + + @Mock + private lateinit var mockMainHandler: Handler + + @Mock + private lateinit var mockPosturesHelper: PosturesHelper + + @Before + fun setup() { + whenever(mockSecureSettings.getStringForUser(any(), anyInt())).thenReturn("") + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun createInstance_refactorFlagEnabled_returnsRefactoredManager() { + val manager = + DeviceStateAutoRotateSettingManagerProvider.createInstance( + context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper + ) + + assertThat(manager).isInstanceOf(DeviceStateAutoRotateSettingManagerImpl::class.java) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun createInstance_refactorFlagDisabled_returnsLegacyManager() { + val manager = + DeviceStateAutoRotateSettingManagerProvider.createInstance( + context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper + ) + + assertThat(manager).isInstanceOf(DeviceStateRotationLockSettingsManager::class.java) + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java index baebaf7dfef0..b23ea5f1786f 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java @@ -151,8 +151,8 @@ public class DeviceStateRotationLockSettingsManagerTest { new String[]{"2:1", "1:0:1", "0:2"}); List<SettableDeviceState> settableDeviceStates = - DeviceStateRotationLockSettingsManager.getInstance( - mMockContext).getSettableDeviceStates(); + new DeviceStateRotationLockSettingsManager(mMockContext, + mFakeSecureSettings).getSettableDeviceStates(); assertThat(settableDeviceStates).containsExactly( new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true), diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index d0f84627f8d8..463e94bfa0d6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -464,6 +464,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.AAPM_USB_DATA_PROTECTION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_APP_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 2273b4f81eea..527a1f16a84f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -16,6 +16,8 @@ package com.android.providers.settings; +import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingUtils.isDeviceStateRotationLockEnabled; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -49,7 +51,6 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.LocalePicker; import com.android.server.backup.Flags; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import java.io.FileNotFoundException; import java.util.ArrayList; @@ -348,7 +349,7 @@ public class SettingsHelper { private boolean shouldSkipAutoRotateRestore() { // When device state based auto rotation settings are available, let's skip the restoring // of the standard auto rotation settings to avoid conflicting setting values. - return DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(mContext); + return isDeviceStateRotationLockEnabled(mContext); } public String onBackupValue(String name, String value) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 7179cbdf93fb..e9b6c73cb800 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -642,6 +642,7 @@ public class SettingsBackupTest { private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS = newHashSet( + Settings.Secure.AAPM_USB_DATA_PROTECTION, Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O. Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7f4544379cd3..2b17ae48132c 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -220,6 +220,7 @@ filegroup { "tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt", "tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt", "tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.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/ui/view/ModernStatusBarWifiViewTest.kt", @@ -356,7 +357,9 @@ filegroup { "tests/src/**/systemui/qs/tiles/AlarmTileTest.kt", "tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt", + "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt", diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp index fb1f715bc68f..172f687ef6fd 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp @@ -40,6 +40,7 @@ android_app { "com_android_systemui_flags_lib", "SettingsLibDisplayUtils", "SettingsLibSettingsTheme", + "SystemUI-shared-utils", "com_android_a11y_menu_flags_lib", "//frameworks/libs/systemui:view_capture", ], diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java index a60778658c59..db2fbd96408c 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java @@ -22,8 +22,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; -import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance; - import static java.lang.Math.max; import android.animation.Animator; @@ -55,11 +53,11 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.UiContext; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; import com.android.systemui.accessibility.accessibilitymenu.R; import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment; import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; import java.util.ArrayList; import java.util.List; @@ -145,9 +143,7 @@ public class A11yMenuOverlayLayout { final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY); final Context uiContext = mService.createWindowContext( display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null); - final ViewCaptureAwareWindowManager windowManager = - getViewCaptureAwareWindowManagerInstance(uiContext, - com.android.systemui.Flags.enableViewCaptureTracing()); + final WindowManager windowManager = WindowManagerUtils.getWindowManager(uiContext); mLayout = new A11yMenuFrameLayout(uiContext); updateLayoutPosition(uiContext); inflateLayoutAndSetOnTouchListener(mLayout, uiContext); @@ -162,8 +158,7 @@ public class A11yMenuOverlayLayout { public void clearLayout() { if (mLayout != null) { - ViewCaptureAwareWindowManager windowManager = getViewCaptureAwareWindowManagerInstance( - mLayout.getContext(), com.android.systemui.Flags.enableViewCaptureTracing()); + WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext()); if (windowManager != null) { windowManager.removeView(mLayout); } @@ -178,7 +173,7 @@ public class A11yMenuOverlayLayout { return; } updateLayoutPosition(mLayout.getContext()); - WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class); + WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext()); if (windowManager != null) { windowManager.updateViewLayout(mLayout, mLayoutParameter); } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java index b899c45b0f7e..842834b5fc27 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java @@ -36,6 +36,7 @@ import com.android.systemui.accessibility.accessibilitymenu.R; import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment; import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuFooter.A11yMenuFooterCallBack; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; import java.util.ArrayList; import java.util.List; @@ -292,8 +293,8 @@ public class A11yMenuViewPager { // Keeps footer window height unchanged no matter the density is changed. mA11yMenuFooter.adjustFooterToDensityScale(densityScale); // Adjust the view pager height for system bar and display cutout insets. - WindowManager windowManager = mA11yMenuLayout.getContext() - .getSystemService(WindowManager.class); + WindowManager windowManager = WindowManagerUtils + .getWindowManager(mA11yMenuLayout.getContext()); WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics(); Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility( WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 7800a5059092..9da07170430f 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -2172,3 +2172,17 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "extended_apps_shortcut_category" + namespace: "systemui" + description: "Allow users to add shortcuts to open apps that are not present in the apps category in shortcut helper by default" + bug: "394290928" +} + +flag { + name: "use_aad_prox_sensor" + namespace: "systemui" + description: "Use AAD proximity sensor if flag is enabled and sensor is present" + bug: "402534470" +} 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 1fb7901dcb59..440a81fc2152 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -1088,9 +1088,8 @@ constructor( if (!success) finishedCallback?.onAnimationFinished() } } else { - // This should never happen, as either the controller or factory should always be - // defined. This final call is for safety in case something goes wrong. - Log.wtf(TAG, "initAndRun with neither a controller nor factory") + // This happens when onDisposed() has already been called due to the animation being + // cancelled. Only issue the callback. finishedCallback?.onAnimationFinished() } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt index 068218a0053a..b30e12f073ad 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt @@ -55,6 +55,7 @@ import com.android.compose.animation.scene.LowestZIndexContentPicker import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.mechanics.behavior.VerticalExpandContainerSpec import com.android.mechanics.behavior.verticalExpandContainerBackground +import com.android.systemui.Flags import com.android.systemui.res.R import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion @@ -189,9 +190,17 @@ object OverlayShade { } object Colors { - val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.2f) + val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.3f) val PanelBackground: Color - @Composable @ReadOnlyComposable get() = MaterialTheme.colorScheme.surfaceContainer + @Composable + @ReadOnlyComposable + get() { + return if (Flags.notificationShadeBlur()) { + MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.4f) + } else { + MaterialTheme.colorScheme.surfaceContainer + } + } } object Dimensions { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index aa95abb3528f..0b0088926aae 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -73,14 +73,10 @@ import android.widget.ImageView; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; -import kotlin.Lazy; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -108,8 +104,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @Mock private MagnificationModeSwitch.ClickListener mClickListener; - @Mock - private Lazy<ViewCapture> mLazyViewCapture; private TestableWindowManager mWindowManager; private ViewPropertyAnimator mViewPropertyAnimator; private MagnificationModeSwitch mMagnificationModeSwitch; @@ -123,7 +117,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mContext = Mockito.spy(getContext()); final WindowManager wm = mContext.getSystemService(WindowManager.class); mWindowManager = spy(new TestableWindowManager(wm)); - mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); mSpyImageView = Mockito.spy(new ImageView(mContext)); mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate()); @@ -139,10 +132,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { return null; }).when(mSfVsyncFrameProvider).postFrameCallback( any(Choreographer.FrameCallback.class)); - ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager, - mLazyViewCapture, false); - mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView, - mSfVsyncFrameProvider, mClickListener, vwm); + mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mWindowManager, + mSpyImageView, mSfVsyncFrameProvider, mClickListener); assertNotNull(mTouchListener); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java index 3cd3fefb8ef0..02ec5aac120c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java @@ -23,11 +23,11 @@ import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; import android.testing.TestableLooper; +import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize; @@ -58,15 +58,15 @@ public class MagnificationSettingsControllerTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private WindowManager mWindowManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); mMagnificationSettingsController = new MagnificationSettingsController( mContext, mSfVsyncFrameProvider, - mMagnificationSettingControllerCallback, mSecureSettings, - mWindowMagnificationSettings, mViewCaptureAwareWindowManager); + mMagnificationSettingControllerCallback, mSecureSettings, mWindowManager, + mWindowMagnificationSettings); } @After diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java index 12c866f0adb2..463bfe5ae73f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java @@ -27,14 +27,15 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.Point; +import android.os.Looper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -48,7 +49,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class MirrorWindowControlTest extends SysuiTestCase { - @Mock ViewCaptureAwareWindowManager mWindowManager; + @Mock WindowManager mWindowManager; View mView; int mViewWidth; int mViewHeight; @@ -69,8 +70,12 @@ public class MirrorWindowControlTest extends SysuiTestCase { return null; }).when(mWindowManager).addView(any(View.class), any(LayoutParams.class)); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + mStubMirrorWindowControl = new StubMirrorWindowControl(getContext(), mView, mViewWidth, - mViewHeight); + mViewHeight, mWindowManager); } @Test @@ -122,8 +127,9 @@ public class MirrorWindowControlTest extends SysuiTestCase { boolean mInvokeOnCreateView = false; - StubMirrorWindowControl(Context context, View view, int width, int height) { - super(context); + StubMirrorWindowControl(Context context, View view, int width, int height, + WindowManager windowManager) { + super(context, windowManager); mView = view; mWidth = width; mHeight = height; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java index e1e515eb31f5..b1c837a99f3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java @@ -24,11 +24,11 @@ import android.provider.Settings; import android.testing.TestableLooper; import android.view.Display; import android.view.View; +import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.SysuiTestCase; import org.junit.After; @@ -51,8 +51,6 @@ public class ModeSwitchesControllerTest extends SysuiTestCase { private View mSpyView; @Mock private MagnificationModeSwitch.ClickListener mListener; - @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; @Before @@ -61,8 +59,9 @@ public class ModeSwitchesControllerTest extends SysuiTestCase { mSupplier = new FakeSwitchSupplier(mContext.getSystemService(DisplayManager.class)); mModeSwitchesController = new ModeSwitchesController(mSupplier); mModeSwitchesController.setClickListenerDelegate(mListener); - mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, mModeSwitchesController, - mViewCaptureAwareWindowManager)); + WindowManager wm = mContext.getSystemService(WindowManager.class); + mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, wm, + mModeSwitchesController)); mSpyView = Mockito.spy(new View(mContext)); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java index 6edf94939010..95ebd8190e9c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java @@ -72,7 +72,7 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase { stubWindowManager); final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance, mockSecureSettings)); - mInteractView = spy(new DragToInteractView(mContext)); + mInteractView = spy(new DragToInteractView(mContext, stubWindowManager)); mDismissView = spy(new DismissView(mContext)); if (Flags.floatingMenuDragToEdit()) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index fff6def52803..572d140b850d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -101,7 +101,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase { mStubMenuView.setTranslationY(0); mMenuAnimationController = spy(new MenuAnimationController( mStubMenuView, stubMenuViewAppearance)); - mInteractView = spy(new DragToInteractView(mContext)); + mInteractView = spy(new DragToInteractView(mContext, windowManager)); mDismissView = spy(new DismissView(mContext)); if (Flags.floatingMenuDragToEdit()) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java index 4f043109a534..b5fa52570d6a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java @@ -39,15 +39,11 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Lazy; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -78,16 +74,11 @@ public class MenuViewLayerControllerTest extends SysuiTestCase { @Mock private WindowMetrics mWindowMetrics; - @Mock - private Lazy<ViewCapture> mLazyViewCapture; - private MenuViewLayerController mMenuViewLayerController; @Before public void setUp() throws Exception { final WindowManager wm = mContext.getSystemService(WindowManager.class); - final ViewCaptureAwareWindowManager viewCaptureAwareWm = new ViewCaptureAwareWindowManager( - mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false); doAnswer(invocation -> wm.getMaximumWindowMetrics()).when( mWindowManager).getMaximumWindowMetrics(); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); @@ -95,8 +86,8 @@ public class MenuViewLayerControllerTest extends SysuiTestCase { when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340)); when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets()); mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager, - viewCaptureAwareWm, mAccessibilityManager, mSecureSettings, - mock(NavigationModeController.class), mHearingAidDeviceManager); + mAccessibilityManager, mSecureSettings, mock(NavigationModeController.class), + mHearingAidDeviceManager); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 446891a7873e..c4fafc192260 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -41,7 +41,6 @@ import android.widget.ScrollView import androidx.constraintlayout.widget.ConstraintLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture import com.android.internal.jank.InteractionJankMonitor import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN @@ -116,7 +115,6 @@ open class AuthContainerViewTest : SysuiTestCase() { @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var activityTaskManager: ActivityTaskManager @Mock private lateinit var accessibilityManager: AccessibilityManager - @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture> private lateinit var displayRepository: FakeDisplayRepository private lateinit var displayStateInteractor: DisplayStateInteractor @@ -689,7 +687,6 @@ open class AuthContainerViewTest : SysuiTestCase() { { credentialViewModel }, fakeExecutor, vibrator, - lazyViewCapture, msdlPlayer, ) { override fun postOnAnimation(runnable: Runnable) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index a1a2aa70d869..f4185ee48510 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -83,7 +83,6 @@ import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; import com.android.internal.R; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; @@ -100,11 +99,10 @@ import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.google.android.msdl.domain.MSDLPlayer; -import dagger.Lazy; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -175,8 +173,6 @@ public class AuthControllerTest extends SysuiTestCase { private PromptViewModel mPromptViewModel; @Mock private UdfpsUtils mUdfpsUtils; - @Mock - private Lazy<ViewCapture> mLazyViewCapture; @Captor private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; @@ -198,6 +194,8 @@ public class AuthControllerTest extends SysuiTestCase { private KeyguardManager mKeyguardManager; @Mock private MSDLPlayer mMSDLPlayer; + @Mock + private WindowManagerProvider mWindowManagerProvider; private TestableContext mContextSpy; private Execution mExecution; @@ -1198,7 +1196,8 @@ public class AuthControllerTest extends SysuiTestCase { when(mDisplayManager.getDisplay(displayId)).thenReturn(mockDisplay); } doReturn(mockDisplayContext).when(mContextSpy).createDisplayContext(mockDisplay); - when(mockDisplayContext.getSystemService(WindowManager.class)).thenReturn(mockDisplayWM); + when(mWindowManagerProvider.getWindowManager(mockDisplayContext)) + .thenReturn(mockDisplayWM); return mockDisplayWM; } @@ -1214,7 +1213,7 @@ public class AuthControllerTest extends SysuiTestCase { () -> mLogContextInteractor, () -> mPromptSelectionInteractor, () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor, mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager, - mLazyViewCapture, mMSDLPlayer); + mMSDLPlayer, mWindowManagerProvider); } @Override diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index a2f5a30a20ff..675c9deaff23 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -45,12 +45,12 @@ import android.view.LayoutInflater; import android.view.Surface; import android.view.View; import android.view.ViewRootImpl; +import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; @@ -129,7 +129,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private FingerprintManager mFingerprintManager; @Mock - private ViewCaptureAwareWindowManager mWindowManager; + private WindowManager mWindowManager; @Mock private StatusBarStateController mStatusBarStateController; @Mock diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt index 95334b5aaf09..856a62e3f5a7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt @@ -25,9 +25,10 @@ import com.android.systemui.common.data.repository.fake import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN import com.android.systemui.communal.data.model.SuppressionReason +import com.android.systemui.communal.posturing.data.model.PositionState import com.android.systemui.communal.posturing.data.repository.fake import com.android.systemui.communal.posturing.data.repository.posturingRepository -import com.android.systemui.communal.posturing.shared.model.PosturedState +import com.android.systemui.communal.posturing.domain.interactor.advanceTimeBySlidingWindowAndRun import com.android.systemui.dock.DockManager import com.android.systemui.dock.fakeDockManager import com.android.systemui.kosmos.Kosmos @@ -127,7 +128,12 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() { ) batteryRepository.fake.setDevicePluggedIn(true) - posturingRepository.fake.setPosturedState(PosturedState.NotPostured) + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.NotPostured(confidence = 1f), + ) + ) assertThat(shouldAutoOpen).isFalse() assertThat(suppressionReason) @@ -135,7 +141,13 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() { SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN) ) - posturingRepository.fake.setPosturedState(PosturedState.Postured(1f)) + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + advanceTimeBySlidingWindowAndRun() assertThat(shouldAutoOpen).isTrue() assertThat(suppressionReason).isNull() } @@ -153,7 +165,7 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() { ) batteryRepository.fake.setDevicePluggedIn(true) - posturingRepository.fake.setPosturedState(PosturedState.Postured(1f)) + posturingRepository.fake.emitPositionState(PositionState()) fakeDockManager.setIsDocked(true) assertThat(shouldAutoOpen).isFalse() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt index 0df8834618d5..b4708d97c4c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt @@ -16,22 +16,39 @@ package com.android.systemui.communal.posturing.domain.interactor +import android.hardware.Sensor +import android.hardware.TriggerEventListener +import android.platform.test.annotations.EnableFlags +import android.service.dreams.Flags.FLAG_ALLOW_DREAM_WHEN_POSTURED import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.posturing.data.model.PositionState import com.android.systemui.communal.posturing.data.repository.fake import com.android.systemui.communal.posturing.data.repository.posturingRepository import com.android.systemui.communal.posturing.shared.model.PosturedState +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.advanceTimeBy import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos +import com.android.systemui.util.sensors.asyncSensorManager import com.google.common.truth.Truth.assertThat +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.milliseconds import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub @SmallTest @RunWith(AndroidJUnit4::class) +@EnableFlags(FLAG_ALLOW_DREAM_WHEN_POSTURED) class PosturingInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() @@ -44,8 +61,166 @@ class PosturingInteractorTest : SysuiTestCase() { val postured by collectLastValue(underTest.postured) assertThat(postured).isFalse() - posturingRepository.fake.setPosturedState(PosturedState.Postured(1f)) + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + + advanceTimeBySlidingWindowAndRun() + assertThat(postured).isTrue() + } + + @Test + fun testLowConfidenceOrientation() = + kosmos.runTest { + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 0.2f), + ) + ) + + advanceTimeBySlidingWindowAndRun() + assertThat(postured).isFalse() + } + + @Test + fun testLowConfidenceStationary() = + kosmos.runTest { + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 0.2f), + ) + ) + + advanceTimeBySlidingWindowAndRun() + assertThat(postured).isFalse() + } + + @Test + fun testSlidingWindow() = + kosmos.runTest { + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 0.2f), + ) + ) + + advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2) + runCurrent() + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + assertThat(postured).isFalse() + advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2) + runCurrent() + + // The 0.2 confidence will have fallen out of the sliding window, and we should now flip + // to true. + assertThat(postured).isTrue() + + advanceTimeBy(9999.hours) + // We should remain postured if no other updates are received. + assertThat(postured).isTrue() + } + + @Test + fun testLiftGesture_afterSlidingWindow() = + kosmos.runTest { + val triggerSensor = stubSensorManager() + val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!! + + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + + advanceTimeBySlidingWindowAndRun() + assertThat(postured).isTrue() + + // If we detect a lift gesture, we should transition back to not postured. + triggerSensor(sensor) + assertThat(postured).isFalse() + + advanceTimeBy(9999.hours) + assertThat(postured).isFalse() + } + + @Test + fun testLiftGesture_overridesSlidingWindow() = + kosmos.runTest { + val triggerSensor = stubSensorManager() + val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!! + + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + // Add multiple stationary + postured events to the sliding window. + repeat(100) { + advanceTimeBy(1.milliseconds) + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + } + + assertThat(postured).isTrue() + + // If we detect a lift gesture, we should transition back to not postured immediately. + triggerSensor(sensor) + assertThat(postured).isFalse() + } + + @Test + fun testSignificantMotion_afterSlidingWindow() = + kosmos.runTest { + val triggerSensor = stubSensorManager() + val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)!! + + val postured by collectLastValue(underTest.postured) + assertThat(postured).isFalse() + + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) + + advanceTimeBySlidingWindowAndRun() assertThat(postured).isTrue() + + // If we detect motion, we should transition back to not postured. + triggerSensor(sensor) + assertThat(postured).isFalse() + + advanceTimeBy(9999.hours) + assertThat(postured).isFalse() } @Test @@ -55,12 +230,51 @@ class PosturingInteractorTest : SysuiTestCase() { assertThat(postured).isFalse() underTest.setValueForDebug(PosturedState.NotPostured) - posturingRepository.fake.setPosturedState(PosturedState.Postured(1f)) + posturingRepository.fake.emitPositionState( + PositionState( + stationary = PositionState.StationaryState.Stationary(confidence = 1f), + orientation = PositionState.OrientationState.Postured(confidence = 1f), + ) + ) // Repository value is overridden by debug value assertThat(postured).isFalse() underTest.setValueForDebug(PosturedState.Unknown) + + advanceTimeBySlidingWindowAndRun() assertThat(postured).isTrue() } + + private fun Kosmos.stubSensorManager(): (sensor: Sensor) -> Unit { + val callbacks = mutableMapOf<Sensor, List<TriggerEventListener>>() + val pickupSensor = mock<Sensor>() + val motionSensor = mock<Sensor>() + + asyncSensorManager.stub { + on { getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) } doReturn pickupSensor + on { getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION) } doReturn motionSensor + on { requestTriggerSensor(any(), any()) } doAnswer + { + val callback = it.arguments[0] as TriggerEventListener + val sensor = it.arguments[1] as Sensor + callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } + callback + true + } + on { cancelTriggerSensor(any(), any()) } doAnswer + { + val callback = it.arguments[0] as TriggerEventListener + val sensor = it.arguments[1] as Sensor + callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } - callback + true + } + } + + return { sensor: Sensor -> + val list = callbacks.getOrElse(sensor) { emptyList() } + // Simulate a trigger sensor which unregisters callbacks after triggering. + callbacks[sensor] = emptyList() + list.forEach { it.onTrigger(mock()) } + } + } } 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 b42eddd12e4e..fd99313a17b7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -36,7 +36,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.app.viewcapture.ViewCaptureFactory import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.UiEventLogger @@ -143,7 +142,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mock<ScrimManager> { on { currentController }.thenReturn(mScrimController) } private val mSystemDialogsCloser = mock<SystemDialogsCloser>() private val mDreamOverlayCallbackController = mock<DreamOverlayCallbackController>() - private val mLazyViewCapture = lazy { viewCaptureSpy } private val mViewCaptor = argumentCaptor<View>() private val mTouchHandlersCaptor = argumentCaptor<Set<TouchHandler>>() @@ -156,7 +154,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { private val gestureInteractor = spy(kosmos.gestureInteractor) private lateinit var mCommunalInteractor: CommunalInteractor - private lateinit var mViewCaptureAwareWindowManager: ViewCaptureAwareWindowManager private lateinit var environmentComponents: EnvironmentComponents private lateinit var mService: DreamOverlayService @@ -244,18 +241,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mComplicationComponentFactory, mAmbientTouchComponentFactory, ) - mViewCaptureAwareWindowManager = - ViewCaptureAwareWindowManager( - mWindowManager, - mLazyViewCapture, - isViewCaptureEnabled = false, - ) mService = DreamOverlayService( mContext, mLifecycleOwner, mMainExecutor, - mViewCaptureAwareWindowManager, + mWindowManager, mComplicationComponentFactory, mDreamComplicationComponentFactory, mDreamOverlayComponentFactory, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt index 63229dbb47a4..8fdbf9b45d2e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt @@ -229,7 +229,6 @@ class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization) } @Test - @EnableFlags(FLAG_GLANCEABLE_HUB_V2) fun transitionToDreaming() = kosmos.runTest { fakePowerRepository.updateWakefulness( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt index 8a11be0ab8fa..b1427f21345c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt @@ -219,17 +219,13 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.addSelectedUserMediaEntry(playingData1) underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1)) underTest.addSelectedUserMediaEntry(playingData2) - underTest.addMediaDataLoadingState( - MediaDataLoadingModel.Loaded(playingInstanceId2, false) - ) + underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2)) assertThat(currentMedia?.size).isEqualTo(2) assertThat(currentMedia) .containsExactly( MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)), - MediaCommonModel.MediaControl( - MediaDataLoadingModel.Loaded(playingInstanceId2, false) - ), + MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)), ) .inOrder() @@ -238,9 +234,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() { assertThat(currentMedia?.size).isEqualTo(2) assertThat(currentMedia) .containsExactly( - MediaCommonModel.MediaControl( - MediaDataLoadingModel.Loaded(playingInstanceId2, false) - ), + MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)), MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)), ) .inOrder() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt index faa62c2febc1..45128e23c900 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt @@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.systemui.SysuiTestCase -import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel +import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -37,8 +37,8 @@ class MediaDiffUtilTest : SysuiTestCase() { @Test fun newMediaControlAdded() { - val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true) - val oldList = listOf<MediaCommonViewModel>() + val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123)) + val oldList = listOf<MediaControlViewModel>() val newList = listOf(mediaControl) val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) val mediaLoadedListUpdateCallback = @@ -56,18 +56,18 @@ class MediaDiffUtilTest : SysuiTestCase() { @Test fun updateMediaControl_contentChanged() { - val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true) + val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123)) val oldList = listOf(mediaControl) - val newList = listOf(mediaControl.copy(immediatelyUpdateUi = false)) + val newList = listOf(mediaControl.copy()) val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) val mediaLoadedListUpdateCallback = MediaViewModelListUpdateCallback( oldList, newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) }, + { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") }, + { controlViewModel, _ -> assertThat(controlViewModel).isNotEqualTo(mediaControl) }, { fail("Unexpected to remove $it") }, - { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") }, + { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") }, ) DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) @@ -75,8 +75,8 @@ class MediaDiffUtilTest : SysuiTestCase() { @Test fun mediaControlMoved() { - val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true) - val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false) + val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123)) + val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456)) val oldList = listOf(mediaControl1, mediaControl2) val newList = listOf(mediaControl2, mediaControl1) val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) @@ -84,10 +84,10 @@ class MediaDiffUtilTest : SysuiTestCase() { MediaViewModelListUpdateCallback( oldList, newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") }, + { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") }, + { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") }, { fail("Unexpected to remove $it") }, - { commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) }, + { controlViewModel, _, _ -> assertThat(controlViewModel).isEqualTo(mediaControl1) }, ) DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) @@ -95,34 +95,24 @@ class MediaDiffUtilTest : SysuiTestCase() { @Test fun mediaControlRemoved() { - val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true) + val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123)) val oldList = listOf(mediaControl) - val newList = listOf<MediaCommonViewModel>() + val newList = listOf<MediaControlViewModel>() val mediaLoadedCallback = MediaViewModelCallback(oldList, newList) val mediaLoadedListUpdateCallback = MediaViewModelListUpdateCallback( oldList, newList, - { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") }, - { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") }, - { commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) }, - { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") }, + { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") }, + { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") }, + { controlViewModel -> assertThat(controlViewModel).isEqualTo(mediaControl) }, + { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") }, ) DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback) } - private fun createMediaControl( - instanceId: InstanceId, - immediatelyUpdateUi: Boolean, - ): MediaCommonViewModel.MediaControl { - return MediaCommonViewModel.MediaControl( - instanceId = instanceId, - immediatelyUpdateUi = immediatelyUpdateUi, - controlViewModel = kosmos.mediaControlViewModel, - onAdded = {}, - onRemoved = {}, - onUpdated = {}, - ) + private fun createMediaControl(instanceId: InstanceId): MediaControlViewModel { + return kosmos.mediaControlViewModel.copy(instanceId = instanceId) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt index e56b114dc847..fab5a3cdf8fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt @@ -86,23 +86,23 @@ class MediaCarouselViewModelTest : SysuiTestCase() { loadMediaControl(KEY_2, instanceId2, isPlaying = true) loadMediaControl(KEY, instanceId1, isPlaying = false) - var mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl - var mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl + var mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel + var mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel assertThat(mediaControl2.instanceId).isEqualTo(instanceId2) assertThat(mediaControl1.instanceId).isEqualTo(instanceId1) loadMediaControl(KEY, instanceId1, isPlaying = true) loadMediaControl(KEY_2, instanceId2, isPlaying = false) - mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl - mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl + mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel + mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel assertThat(mediaControl2.instanceId).isEqualTo(instanceId2) assertThat(mediaControl1.instanceId).isEqualTo(instanceId1) underTest.onReorderingAllowed() - mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl - mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl + mediaControl1 = sortedMedia?.get(0) as MediaControlViewModel + mediaControl2 = sortedMedia?.get(1) as MediaControlViewModel assertThat(mediaControl1.instanceId).isEqualTo(instanceId1) assertThat(mediaControl2.instanceId).isEqualTo(instanceId2) } @@ -115,7 +115,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() { loadMediaControl(KEY, instanceId) - val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl + val mediaControl = sortedMedia?.get(0) as MediaControlViewModel assertThat(mediaControl.instanceId).isEqualTo(instanceId) // when media control is added to carousel diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt index 55e52b780488..18b14a4b3752 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt @@ -55,8 +55,6 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { @Before fun setup() { - whenever(mockContext.getSystemService(eq(WindowManager::class.java))) - .thenReturn(windowManager) whenever(mockContext.resources).thenReturn(resources) } @@ -154,7 +152,8 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() { return TaskPreviewSizeProvider( mockContext, windowMetricsProvider, - testConfigurationController + testConfigurationController, + windowManager ) .also { it.addCallback(listener) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java index 09e49eb217b0..0e8c5079579e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java @@ -82,7 +82,6 @@ import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; @@ -218,8 +217,6 @@ public class NavigationBarTest extends SysuiTestCase { @Mock private WindowManager mWindowManager; @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; - @Mock private TelecomManager mTelecomManager; @Mock private InputMethodManager mInputMethodManager; @@ -685,7 +682,6 @@ public class NavigationBarTest extends SysuiTestCase { null, context, mWindowManager, - mViewCaptureAwareWindowManager, () -> mAssistManager, mock(AccessibilityManager.class), deviceProvisionedController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt index 52a0a5445002..7fb66ce21919 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt @@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserActionResult.HideOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays +import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer @@ -77,10 +76,8 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() { underTest.activateIn(this) val action = - (actions?.get(Swipe.Down(fromSource = SceneContainerArea.EndHalf)) as? ShowOverlay) + (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf)) + as? ReplaceByOverlay) assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade) - val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some - assertThat(overlaysToHide).isNotNull() - assertThat(overlaysToHide?.overlays).containsExactly(Overlays.NotificationsShade) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt index 80ce43d61003..ffcd95bc7a4a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt @@ -157,7 +157,7 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { } @Test - fun showMedia_noActiveMedia_false() = + fun showMedia_InactiveMedia_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false)) runCurrent() @@ -166,6 +166,16 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { } @Test + fun showMedia_noMedia_false() = + testScope.runTest { + kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) + kosmos.mediaFilterRepository.clearSelectedUserMedia() + runCurrent() + + assertThat(underTest.showMedia).isFalse() + } + + @Test fun showMedia_qsDisabled_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java index 338ed7596bd6..70ded2a7f21c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java @@ -40,11 +40,11 @@ import android.service.vr.IVrStateCallbacks; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableResources; +import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; import com.android.settingslib.fuelgauge.Estimate; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -54,8 +54,6 @@ import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; -import dagger.Lazy; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -96,7 +94,7 @@ public class PowerUITest extends SysuiTestCase { @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private CommandQueue mCommandQueue; @Mock private IVrManager mVrManager; - @Mock private Lazy<ViewCapture> mLazyViewCapture; + @Mock private WindowManager mWindowManager; @Before public void setup() { @@ -709,7 +707,7 @@ public class PowerUITest extends SysuiTestCase { mWakefulnessLifecycle, mPowerManager, mUserTracker, - mLazyViewCapture); + mWindowManager); mPowerUI.mThermalService = mThermalServiceMock; } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt index 701e55d0759d..d11701570e32 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt @@ -95,11 +95,21 @@ class QuickSettingsContainerViewModelTest : SysuiTestCase() { } @Test - fun showMedia_noActiveMedia_false() = + fun showMedia_InactiveMedia_true() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false)) runCurrent() + assertThat(underTest.showMedia).isTrue() + } + + @Test + fun showMedia_noMedia_false() = + testScope.runTest { + kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) + kosmos.mediaFilterRepository.clearSelectedUserMedia() + runCurrent() + assertThat(underTest.showMedia).isFalse() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt index b98059a1fe90..882d296d94b4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt @@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserActionResult.HideOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays +import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer @@ -90,11 +89,8 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() { underTest.activateIn(this) val action = - (actions?.get(Swipe.Down(fromSource = SceneContainerArea.StartHalf)) - as? ShowOverlay) + (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf)) + as? ReplaceByOverlay) assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade) - val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some - assertThat(overlaysToHide).isNotNull() - assertThat(overlaysToHide?.overlays).containsExactly(Overlays.QuickSettingsShade) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt index a09e5cd9de9b..7536bb3cc02a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt @@ -32,6 +32,8 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftE import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftHalf import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightEdge import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightHalf +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeLeftHalf +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeRightHalf import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartEdge import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartHalf import com.google.common.truth.Truth.assertThat @@ -55,9 +57,9 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() { } @Test - fun source_swipeVerticallyOnTopLeft_detectsLeftHalf() { + fun source_swipeVerticallyOnTopLeft_detectsTopEdgeLeftHalf() { val detectedEdge = swipeVerticallyFrom(x = 1, y = edgeSize - 1) - assertThat(detectedEdge).isEqualTo(LeftHalf) + assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf) } @Test @@ -69,7 +71,7 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() { @Test fun source_swipeVerticallyOnTopRight_detectsRightHalf() { val detectedEdge = swipeVerticallyFrom(x = screenWidth - 1, y = edgeSize - 1) - assertThat(detectedEdge).isEqualTo(RightHalf) + assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf) } @Test @@ -79,14 +81,26 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() { } @Test - fun source_swipeVerticallyToLeftOfSplit_detectsLeftHalf() { + fun source_swipeVerticallyOnTopEdge_ToLeftOfSplit_detectsTopEdgeLeftHalf() { val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf) + } + + @Test + fun source_swipeVerticallyBelowTopEdge_ToLeftOfSplit_detectsLeftHalf() { + val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize + 1) assertThat(detectedEdge).isEqualTo(LeftHalf) } @Test - fun source_swipeVerticallyToRightOfSplit_detectsRightHalf() { + fun source_swipeVerticallyOnTopEdge_toRightOfSplit_detectsTopEdgeRightHalf() { val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf) + } + + @Test + fun source_swipeVerticallyBelowTopEdge_toRightOfSplit_detectsRightHalf() { + val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize + 1) assertThat(detectedEdge).isEqualTo(RightHalf) } 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 94db429c2225..676e1ea5321a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -172,6 +172,7 @@ import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel; import com.android.wm.shell.animation.FlingAnimationUtils; @@ -296,6 +297,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper; @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector; @Mock protected SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor; + @Mock private WindowManagerProvider mWindowManagerProvider; protected final int mMaxUdfpsBurnInOffsetY = 5; protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); protected KeyguardClockInteractor mKeyguardClockInteractor; @@ -672,7 +674,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mCastController, new ResourcesSplitShadeStateController(), () -> mKosmos.getCommunalTransitionViewModel(), - () -> mLargeScreenHeaderHelper + () -> mLargeScreenHeaderHelper, + mWindowManagerProvider ); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 3788049256a2..fd17b7a32fd3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -50,7 +50,6 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.colorextraction.ColorExtractor; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -100,7 +99,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Rule public final CheckFlagsRule checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - @Mock private ViewCaptureAwareWindowManager mWindowManager; + @Mock private WindowManager mWindowManager; @Mock private DozeParameters mDozeParameters; @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy( new NotificationShadeWindowView(mContext, null)); 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 7433267ab3b0..db0c07c50dc6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.Looper; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.MetricsLogger; @@ -79,6 +80,7 @@ import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupReposi import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import dagger.Lazy; @@ -147,6 +149,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { @Mock protected UserSwitcherInteractor mUserSwitcherInteractor; @Mock protected SelectedUserInteractor mSelectedUserInteractor; @Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper; + @Mock protected WindowManagerProvider mWindowManagerProvider; protected FakeDisableFlagsRepository mDisableFlagsRepository = mKosmos.getFakeDisableFlagsRepository(); @@ -232,6 +235,9 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { mMainHandler = new Handler(Looper.getMainLooper()); + WindowManager wm = mContext.getSystemService(WindowManager.class); + when(mWindowManagerProvider.getWindowManager(mContext)).thenReturn(wm); + mQsController = new QuickSettingsControllerImpl( mPanelViewControllerLazy, mPanelView, @@ -268,7 +274,8 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { mCastController, splitShadeStateController, () -> mKosmos.getCommunalTransitionViewModel(), - () -> mLargeScreenHeaderHelper + () -> mLargeScreenHeaderHelper, + mWindowManagerProvider ); mQsController.init(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt index 202d5cff95d7..233efbb97d4e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt @@ -56,9 +56,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @DisableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_flagOff_noNotifs() = + fun allNotificationChips_flagOff_noNotifs() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs( listOf( @@ -75,9 +75,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_noNotifs_empty() = + fun allNotificationChips_noNotifs_empty() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs(emptyList()) @@ -87,9 +87,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) - fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() = + fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs( listOf( @@ -106,9 +106,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME) - fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() = + fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs( listOf( @@ -125,9 +125,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_onePromotedNotif_statusBarIconViewMatches() = + fun allNotificationChips_onePromotedNotif_statusBarIconViewMatches() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val icon = mock<StatusBarIconView>() setNotifs( @@ -147,9 +147,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_onlyForPromotedNotifs() = + fun allNotificationChips_onlyForPromotedNotifs() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val firstIcon = mock<StatusBarIconView>() val secondIcon = mock<StatusBarIconView>() @@ -182,11 +182,11 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_onlyForNotVisibleApps() = + fun allNotificationChips_appVisibilityInfoCorrect() = kosmos.runTest { activityManagerRepository.fake.startingIsAppVisibleValue = false - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val uid = 433 setNotifs( @@ -195,27 +195,30 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = "notif", uid = uid, statusBarChipIcon = mock<StatusBarIconView>(), - promotedContent = PromotedNotificationContentBuilder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false) assertThat(latest).hasSize(1) + assertThat(latest!![0].isAppVisible).isFalse() activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true) - assertThat(latest).isEmpty() + assertThat(latest).hasSize(1) + assertThat(latest!![0].isAppVisible).isTrue() activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false) assertThat(latest).hasSize(1) + assertThat(latest!![0].isAppVisible).isFalse() } /** Regression test for b/388521980. */ @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() = + fun allNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs( listOf( @@ -243,9 +246,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_notifUpdatesGoThrough() = + fun allNotificationChips_notifUpdatesGoThrough() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val firstIcon = mock<StatusBarIconView>() val secondIcon = mock<StatusBarIconView>() @@ -293,9 +296,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_promotedNotifDisappearsThenReappears() = + fun allNotificationChips_promotedNotifDisappearsThenReappears() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) setNotifs( listOf( @@ -335,9 +338,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_sortedByFirstAppearanceTime() = + fun allNotificationChips_sortedByFirstAppearanceTime() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val firstIcon = mock<StatusBarIconView>() val secondIcon = mock<StatusBarIconView>() @@ -411,9 +414,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() = + fun allNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100) val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200) @@ -466,9 +469,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_sortedByLastAppVisibleTime() = + fun allNotificationChips_sortedByLastAppVisibleTime() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100) val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200) @@ -496,15 +499,19 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { fakeSystemClock.advanceTime(1000) activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = true) - // THEN notif2 is no longer shown - assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder() + // THEN notif2 is ranked above notif1 because it was more recently visible + assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder() + assertThat(latest!![0].isAppVisible).isTrue() // notif2 + assertThat(latest!![1].isAppVisible).isFalse() // notif1 // WHEN notif2's app is no longer visible fakeSystemClock.advanceTime(1000) activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false) - // THEN notif2 is ranked above notif1 because it was more recently visible + // THEN notif2 is still ranked above notif1 assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder() + assertThat(latest!![0].isAppVisible).isFalse() // notif2 + assertThat(latest!![1].isAppVisible).isFalse() // notif1 // WHEN the app associated with notif1 becomes visible then un-visible fakeSystemClock.advanceTime(1000) @@ -518,9 +525,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_newNotificationTakesPriorityOverLastAppVisible() = + fun allNotificationChips_newNotificationTakesPriorityOverLastAppVisible() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100) val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200) @@ -574,9 +581,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_fullSort() = + fun allNotificationChips_fullSort() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100) val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200) @@ -689,9 +696,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun shownNotificationChips_notifChangesKey() = + fun allNotificationChips_notifChangesKey() = kosmos.runTest { - val latest by collectLastValue(underTest.shownNotificationChips) + val latest by collectLastValue(underTest.allNotificationChips) val firstIcon = mock<StatusBarIconView>() val secondIcon = mock<StatusBarIconView>() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index eecdbbfd408b..660f0b52f464 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -24,6 +24,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY import com.android.systemui.SysuiTestCase +import com.android.systemui.activity.data.repository.activityManagerRepository +import com.android.systemui.activity.data.repository.fake import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -286,6 +288,84 @@ class NotifChipsViewModelTest : SysuiTestCase() { } @Test + fun chips_appStartsAsVisible_isHiddenTrue() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = true + + val latest by collectLastValue(underTest.chips) + + val uid = 433 + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + uid = uid, + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0].isHidden).isTrue() + } + + @Test + fun chips_appStartsAsNotVisible_isHiddenFalse() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = false + + val latest by collectLastValue(underTest.chips) + + val uid = 433 + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + uid = uid, + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0].isHidden).isFalse() + } + + @Test + fun chips_isHidden_changesBasedOnAppVisibility() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = false + + val latest by collectLastValue(underTest.chips) + + val uid = 433 + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + uid = uid, + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), + ) + ) + ) + + activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false) + assertThat(latest).hasSize(1) + assertThat(latest!![0].isHidden).isFalse() + + activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true) + assertThat(latest).hasSize(1) + assertThat(latest!![0].isHidden).isTrue() + + activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false) + assertThat(latest).hasSize(1) + assertThat(latest!![0].isHidden).isFalse() + } + + @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) fun chips_hasShortCriticalText_usesTextInsteadOfTime() = kosmos.runTest { @@ -422,6 +502,42 @@ class NotifChipsViewModelTest : SysuiTestCase() { } @Test + fun chips_basicTime_respectsIsAppVisible() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = false + + val latest by collectLastValue(underTest.chips) + val currentTime = 3.minutes.inWholeMilliseconds + fakeSystemClock.setCurrentTimeMillis(currentTime) + + val promotedContentBuilder = + PromotedNotificationContentBuilder("notif").applyToShared { + this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds) + } + val uid = 3 + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + uid = 3, + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = promotedContentBuilder.build(), + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0]) + .isInstanceOf(OngoingActivityChipModel.Active.ShortTimeDelta::class.java) + assertThat(latest!![0].isHidden).isFalse() + + activityManagerRepository.fake.setIsAppVisible(uid = uid, isAppVisible = true) + + assertThat(latest!![0].isHidden).isTrue() + } + + @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) fun chips_basicTime_timeLessThanOneMinInFuture_isIconOnly() = kosmos.runTest { @@ -579,6 +695,48 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) + fun chips_countUpTime_respectsIsAppVisible() = + kosmos.runTest { + activityManagerRepository.fake.startingIsAppVisibleValue = true + + val latest by collectLastValue(underTest.chips) + val currentTime = 30.minutes.inWholeMilliseconds + fakeSystemClock.setCurrentTimeMillis(currentTime) + + val currentElapsed = + currentTime + fakeSystemClock.elapsedRealtime() - + fakeSystemClock.currentTimeMillis() + + val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds + + val promotedContentBuilder = + PromotedNotificationContentBuilder("notif").applyToShared { + this.time = + When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false) + } + val uid = 6 + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + uid = uid, + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = promotedContentBuilder.build(), + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat(latest!![0].isHidden).isTrue() + + activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false) + + assertThat(latest!![0].isHidden).isFalse() + } + + @Test + @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) fun chips_countDownTime_isTimer() = kosmos.runTest { val latest by collectLastValue(underTest.chips) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt index 5c749e6e35d6..873357c2201e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt @@ -21,8 +21,9 @@ import android.service.notification.StatusBarNotification 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.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -35,7 +36,6 @@ import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -43,17 +43,14 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RenderNotificationsListInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() - private val testScope = kosmos.testScope - private val notifsRepository = kosmos.activeNotificationListRepository - private val notifsInteractor = kosmos.activeNotificationsInteractor - private val underTest = - RenderNotificationListInteractor(notifsRepository, sectionStyleProvider = mock(), context) + private val Kosmos.outputInteractor by Kosmos.Fixture { activeNotificationsInteractor } + private val Kosmos.underTest by Kosmos.Fixture { renderNotificationListInteractor } @Test fun setRenderedList_preservesOrdering() = - testScope.runTest { - val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications) + kosmos.runTest { + val notifs by collectLastValue(outputInteractor.topLevelRepresentativeNotifications) val keys = (1..50).shuffled().map { "$it" } val entries = keys.map { mockNotificationEntry(key = it) } underTest.setRenderedList(entries) @@ -65,8 +62,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { @Test fun setRenderList_flatMapsRankings() = - testScope.runTest { - val ranks by collectLastValue(notifsInteractor.activeNotificationRanks) + kosmos.runTest { + val ranks by collectLastValue(outputInteractor.activeNotificationRanks) val single = mockNotificationEntry("single", 0) val group = @@ -90,8 +87,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { @Test fun setRenderList_singleItems_mapsRankings() = - testScope.runTest { - val actual by collectLastValue(notifsInteractor.activeNotificationRanks) + kosmos.runTest { + val actual by collectLastValue(outputInteractor.activeNotificationRanks) val expected = (0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap() @@ -104,8 +101,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { @Test fun setRenderList_groupWithNoSummary_flatMapsRankings() = - testScope.runTest { - val actual by collectLastValue(notifsInteractor.activeNotificationRanks) + kosmos.runTest { + val actual by collectLastValue(outputInteractor.activeNotificationRanks) val expected = (0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap() @@ -124,8 +121,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME) fun setRenderList_setsPromotionContent() = - testScope.runTest { - val actual by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications) + kosmos.runTest { + val actual by collectLastValue(outputInteractor.topLevelRepresentativeNotifications) val notPromoted1 = mockNotificationEntry("key1", promotedContent = null) val promoted2 = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt new file mode 100644 index 000000000000..3cf787d50d2f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt @@ -0,0 +1,70 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.activated +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.launchKairosNetwork +import com.android.systemui.kairos.stateOf +import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig +import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import org.junit.runner.RunWith +import org.mockito.Mockito + +@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class CarrierMergedConnectionRepositoryKairosAdapterTest : + CarrierMergedConnectionRepositoryTestBase() { + + var job: Job? = null + val kairosNetwork = testScope.backgroundScope.launchKairosNetwork() + + override fun recreateRepo(): MobileConnectionRepositoryKairosAdapter { + lateinit var adapter: MobileConnectionRepositoryKairosAdapter + job?.cancel() + Mockito.clearInvocations(telephonyManager) + job = + testScope.backgroundScope.launch { + kairosNetwork.activateSpec { + val repo = activated { + CarrierMergedConnectionRepositoryKairos( + SUB_ID, + logger, + telephonyManager, + wifiRepository, + isInEcmMode = stateOf(false), + ) + } + adapter = + MobileConnectionRepositoryKairosAdapter( + repo, + SystemUiCarrierConfig(SUB_ID, testCarrierConfig()), + ) + Unit + } + } + testScope.runCurrent() // ensure the lateinit is set + return adapter + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt index 8e55f2e9a31a..8a6829cf5d21 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel @@ -43,16 +44,30 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) -class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { +class CarrierMergedConnectionRepositoryTest : CarrierMergedConnectionRepositoryTestBase() { + override fun recreateRepo() = + CarrierMergedConnectionRepository( + SUB_ID, + logger, + telephonyManager, + testScope.backgroundScope.coroutineContext, + testScope.backgroundScope, + wifiRepository, + ) +} + +abstract class CarrierMergedConnectionRepositoryTestBase : SysuiTestCase() { - private lateinit var underTest: CarrierMergedConnectionRepository + protected lateinit var underTest: MobileConnectionRepository - private lateinit var wifiRepository: FakeWifiRepository - @Mock private lateinit var logger: TableLogBuffer - @Mock private lateinit var telephonyManager: TelephonyManager + protected lateinit var wifiRepository: FakeWifiRepository + @Mock protected lateinit var logger: TableLogBuffer + @Mock protected lateinit var telephonyManager: TelephonyManager - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) + protected val testDispatcher = UnconfinedTestDispatcher() + protected val testScope = TestScope(testDispatcher) + + abstract fun recreateRepo(): MobileConnectionRepository @Before fun setUp() { @@ -62,15 +77,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { wifiRepository = FakeWifiRepository() - underTest = - CarrierMergedConnectionRepository( - SUB_ID, - logger, - telephonyManager, - testScope.backgroundScope.coroutineContext, - testScope.backgroundScope, - wifiRepository, - ) + underTest = recreateRepo() } @Test @@ -121,10 +128,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { wifiRepository.setIsWifiDefault(true) wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3) ) assertThat(latest).isEqualTo(3) @@ -141,26 +145,17 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { wifiRepository.setIsWifiEnabled(true) wifiRepository.setIsWifiDefault(true) wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3) ) wifiRepository.setWifiActivity( - DataActivityModel( - hasActivityIn = true, - hasActivityOut = false, - ) + DataActivityModel(hasActivityIn = true, hasActivityOut = false) ) assertThat(latest!!.hasActivityIn).isTrue() assertThat(latest!!.hasActivityOut).isFalse() wifiRepository.setWifiActivity( - DataActivityModel( - hasActivityIn = false, - hasActivityOut = true, - ) + DataActivityModel(hasActivityIn = false, hasActivityOut = true) ) assertThat(latest!!.hasActivityIn).isFalse() @@ -178,10 +173,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { val typeJob = underTest.resolvedNetworkType.onEach { latestType = it }.launchIn(this) wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID + 10, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID + 10, level = 3) ) assertThat(latestLevel).isNotEqualTo(3) @@ -199,10 +191,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this) wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3) ) wifiRepository.setIsWifiEnabled(false) @@ -219,10 +208,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this) wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3) ) wifiRepository.setIsWifiDefault(false) @@ -280,6 +266,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { fun networkName_usesSimOperatorNameAsInitial() = testScope.runTest { whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name") + underTest = recreateRepo() var latest: NetworkNameModel? = null val job = underTest.networkName.onEach { latest = it }.launchIn(this) @@ -293,6 +280,10 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { fun networkName_updatesOnNetworkUpdate() = testScope.runTest { whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name") + underTest = recreateRepo() + + wifiRepository.setIsWifiEnabled(true) + wifiRepository.setIsWifiDefault(true) var latest: NetworkNameModel? = null val job = underTest.networkName.onEach { latest = it }.launchIn(this) @@ -301,10 +292,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { whenever(telephonyManager.simOperatorName).thenReturn("New SIM name") wifiRepository.setWifiNetwork( - WifiNetworkModel.CarrierMerged.of( - subscriptionId = SUB_ID, - level = 3, - ) + WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3) ) assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("New SIM name")) @@ -320,7 +308,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() { assertThat(latest).isTrue() } - private companion object { + companion object { const val SUB_ID = 123 } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt new file mode 100644 index 000000000000..e72d0c27e632 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt @@ -0,0 +1,141 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.SignalIcon +import com.android.settingslib.mobile.MobileIconCarrierIdOverrides +import com.android.systemui.activated +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.Incremental +import com.android.systemui.kairos.State +import com.android.systemui.kairos.asIncremental +import com.android.systemui.kairos.buildSpec +import com.android.systemui.kairos.combine +import com.android.systemui.kairos.launchKairosNetwork +import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosAdapterTest.Companion.wrapRepo +import com.android.systemui.util.mockito.mock +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import org.junit.runner.RunWith + +@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class MobileIconInteractorKairosAdapterTest : MobileIconInteractorTestBase() { + + var job: Job? = null + val kairosNetwork = testScope.backgroundScope.launchKairosNetwork() + + override fun createInteractor(overrides: MobileIconCarrierIdOverrides): MobileIconInteractor { + lateinit var result: MobileIconInteractor + job?.cancel() + job = + testScope.backgroundScope.launch { + kairosNetwork.activateSpec { + val wrapped = wrap(mobileIconsInteractor) + result = + MobileIconInteractorKairosAdapter( + kairosImpl = + activated { + MobileIconInteractorKairosImpl( + defaultSubscriptionHasDataEnabled = + wrapped.activeDataConnectionHasDataEnabled, + alwaysShowDataRatIcon = wrapped.alwaysShowDataRatIcon, + alwaysUseCdmaLevel = wrapped.alwaysUseCdmaLevel, + isSingleCarrier = wrapped.isSingleCarrier, + mobileIsDefault = wrapped.mobileIsDefault, + defaultMobileIconMapping = wrapped.defaultMobileIconMapping, + defaultMobileIconGroup = wrapped.defaultMobileIconGroup, + isDefaultConnectionFailed = + wrapped.isDefaultConnectionFailed, + isForceHidden = wrapped.isForceHidden, + connectionRepository = wrapRepo(connectionRepository), + context = context, + carrierIdOverrides = overrides, + ) + } + ) + Unit + } + } + testScope.runCurrent() // ensure the lateinit is set + return result + } + + /** Allows us to wrap a (likely fake) MobileIconsInteractor into a Kairos version. */ + private fun BuildScope.wrap(interactor: MobileIconsInteractor): MobileIconsInteractorKairos { + val filteredSubscriptions = interactor.filteredSubscriptions.toState(emptyList()) + val icons = interactor.icons.toState() + return InteractorWrapper( + mobileIsDefault = interactor.mobileIsDefault.toState(), + filteredSubscriptions = filteredSubscriptions, + icons = + combine(filteredSubscriptions, icons) { subs, icons -> + subs.zip(icons).associate { (subModel, icon) -> + subModel.subscriptionId to buildSpec { wrap(icon) } + } + } + .asIncremental() + .applyLatestSpecForKey(), + isStackable = interactor.isStackable.toState(), + activeDataConnectionHasDataEnabled = + interactor.activeDataConnectionHasDataEnabled.toState(), + activeDataIconInteractor = + interactor.activeDataIconInteractor.toState().mapLatestBuild { + it?.let { wrap(it) } + }, + alwaysShowDataRatIcon = interactor.alwaysShowDataRatIcon.toState(), + alwaysUseCdmaLevel = interactor.alwaysUseCdmaLevel.toState(), + isSingleCarrier = interactor.isSingleCarrier.toState(), + defaultMobileIconMapping = interactor.defaultMobileIconMapping.toState(), + defaultMobileIconGroup = interactor.defaultMobileIconGroup.toState(), + isDefaultConnectionFailed = interactor.isDefaultConnectionFailed.toState(), + isUserSetUp = interactor.isUserSetUp.toState(), + isForceHidden = interactor.isForceHidden.toState(false), + isDeviceInEmergencyCallsOnlyMode = + interactor.isDeviceInEmergencyCallsOnlyMode.toState(false), + ) + } + + private fun BuildScope.wrap(interactor: MobileIconInteractor): MobileIconInteractorKairos = + // unused in tests + mock() + + private class InteractorWrapper( + override val mobileIsDefault: State<Boolean>, + override val filteredSubscriptions: State<List<SubscriptionModel>>, + override val icons: Incremental<Int, MobileIconInteractorKairos>, + override val isStackable: State<Boolean>, + override val activeDataConnectionHasDataEnabled: State<Boolean>, + override val activeDataIconInteractor: State<MobileIconInteractorKairos?>, + override val alwaysShowDataRatIcon: State<Boolean>, + override val alwaysUseCdmaLevel: State<Boolean>, + override val isSingleCarrier: State<Boolean>, + override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>>, + override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup>, + override val isDefaultConnectionFailed: State<Boolean>, + override val isUserSetUp: State<Boolean>, + override val isForceHidden: State<Boolean>, + override val isDeviceInEmergencyCallsOnlyMode: State<Boolean>, + ) : MobileIconsInteractorKairos +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index 8c70da718c08..974a47587c77 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -22,6 +22,7 @@ import android.telephony.CellSignalStrength import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.telephony.flags.Flags import com.android.settingslib.mobile.MobileIconCarrierIdOverrides import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl import com.android.settingslib.mobile.TelephonyIcons @@ -58,21 +59,40 @@ import org.mockito.ArgumentMatchers.anyString @SmallTest @RunWith(AndroidJUnit4::class) -class MobileIconInteractorTest : SysuiTestCase() { - private val kosmos = testKosmos() +class MobileIconInteractorTest : MobileIconInteractorTestBase() { + override fun createInteractor(overrides: MobileIconCarrierIdOverrides) = + MobileIconInteractorImpl( + testScope.backgroundScope, + mobileIconsInteractor.activeDataConnectionHasDataEnabled, + mobileIconsInteractor.alwaysShowDataRatIcon, + mobileIconsInteractor.alwaysUseCdmaLevel, + mobileIconsInteractor.isSingleCarrier, + mobileIconsInteractor.mobileIsDefault, + mobileIconsInteractor.defaultMobileIconMapping, + mobileIconsInteractor.defaultMobileIconGroup, + mobileIconsInteractor.isDefaultConnectionFailed, + mobileIconsInteractor.isForceHidden, + connectionRepository, + context, + overrides, + ) +} - private lateinit var underTest: MobileIconInteractor - private val mobileMappingsProxy = FakeMobileMappingsProxy() - private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock()) +abstract class MobileIconInteractorTestBase : SysuiTestCase() { + protected val kosmos = testKosmos() - private val connectionRepository = + protected lateinit var underTest: MobileIconInteractor + protected val mobileMappingsProxy = FakeMobileMappingsProxy() + protected val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock()) + + protected val connectionRepository = FakeMobileConnectionRepository( SUB_1_ID, logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"), ) - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) + protected val testDispatcher = UnconfinedTestDispatcher() + protected val testScope = TestScope(testDispatcher) @Before fun setUp() { @@ -835,24 +855,9 @@ class MobileIconInteractorTest : SysuiTestCase() { assertThat(latest!!.level).isEqualTo(0) } - private fun createInteractor( + abstract fun createInteractor( overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl() - ) = - MobileIconInteractorImpl( - testScope.backgroundScope, - mobileIconsInteractor.activeDataConnectionHasDataEnabled, - mobileIconsInteractor.alwaysShowDataRatIcon, - mobileIconsInteractor.alwaysUseCdmaLevel, - mobileIconsInteractor.isSingleCarrier, - mobileIconsInteractor.mobileIsDefault, - mobileIconsInteractor.defaultMobileIconMapping, - mobileIconsInteractor.defaultMobileIconGroup, - mobileIconsInteractor.isDefaultConnectionFailed, - mobileIconsInteractor.isForceHidden, - connectionRepository, - context, - overrides, - ) + ): MobileIconInteractor companion object { private const val GSM_LEVEL = 1 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt new file mode 100644 index 000000000000..787731e14cd1 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt @@ -0,0 +1,223 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.SignalIcon +import com.android.settingslib.mobile.MobileMappings +import com.android.settingslib.mobile.TelephonyIcons +import com.android.systemui.KairosBuilder +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.Events +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.Incremental +import com.android.systemui.kairos.State +import com.android.systemui.kairos.activateKairosActivatable +import com.android.systemui.kairos.asIncremental +import com.android.systemui.kairos.buildSpec +import com.android.systemui.kairos.kairos +import com.android.systemui.kairos.map +import com.android.systemui.kairos.mapValues +import com.android.systemui.kairos.stateOf +import com.android.systemui.kairosBuilder +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.tableLogBufferFactory +import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState +import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType +import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileMappingsProxy +import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository +import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@OptIn(ExperimentalKairosApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class MobileIconsInteractorKairosAdapterTest : MobileIconsInteractorTestBase() { + override fun Kosmos.createInteractor(): MobileIconsInteractor { + val userSetupRepo = FakeUserSetupRepository() + val repoK = + MobileConnectionsRepoWrapper(connectionsRepository).also { + activateKairosActivatable(it) + } + val kairosInteractor = + MobileIconsInteractorKairosImpl( + mobileConnectionsRepo = repoK, + carrierConfigTracker = carrierConfigTracker, + tableLogger = mock(), + connectivityRepository = connectivityRepository, + userSetupRepo = userSetupRepo, + context = context, + featureFlagsClassic = featureFlagsClassic, + ) + .also { activateKairosActivatable(it) } + return MobileIconsInteractorKairosAdapter( + kairosInteractor = kairosInteractor, + repo = connectionsRepository, + repoK = repoK, + kairosNetwork = kairos, + scope = applicationCoroutineScope, + context = context, + mobileMappingsProxy = mobileMappingsProxy, + userSetupRepo = userSetupRepo, + logFactory = tableLogBufferFactory, + ) + .also { + activateKairosActivatable(it) + runCurrent() + } + } + + /** Allows us to wrap a (likely fake) MobileConnectionsRepository into a Kairos version. */ + private class MobileConnectionsRepoWrapper(val unwrapped: MobileConnectionsRepository) : + MobileConnectionsRepositoryKairos, KairosBuilder by kairosBuilder() { + + override val mobileConnectionsBySubId: Incremental<Int, MobileConnectionRepositoryKairos> = + buildIncremental { + unwrapped.subscriptions + .toState() + .map { it.associate { it.subscriptionId to Unit } } + .asIncremental() + .mapValues { (subId, _) -> + buildSpec { wrapRepo(unwrapped.getRepoForSubId(subId)) } + } + .applyLatestSpecForKey() + } + override val subscriptions: State<Collection<SubscriptionModel>> = buildState { + unwrapped.subscriptions.toState() + } + override val activeMobileDataSubscriptionId: State<Int?> = buildState { + unwrapped.activeMobileDataSubscriptionId.toState() + } + override val activeMobileDataRepository: State<MobileConnectionRepositoryKairos?> = + buildState { + unwrapped.activeMobileDataRepository.toState().mapLatestBuild { + it?.let { wrapRepo(it) } + } + } + override val activeSubChangedInGroupEvent: Events<Unit> = buildEvents { + unwrapped.activeSubChangedInGroupEvent.toEvents() + } + override val defaultDataSubId: State<Int?> = buildState { + unwrapped.defaultDataSubId.toState() + } + override val mobileIsDefault: State<Boolean> = buildState { + unwrapped.mobileIsDefault.toState() + } + override val hasCarrierMergedConnection: State<Boolean> = buildState { + unwrapped.hasCarrierMergedConnection.toState(false) + } + override val defaultConnectionIsValidated: State<Boolean> = buildState { + unwrapped.defaultConnectionIsValidated.toState() + } + override val defaultDataSubRatConfig: State<MobileMappings.Config> = buildState { + unwrapped.defaultDataSubRatConfig.toState() + } + override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>> = + buildState { + unwrapped.defaultMobileIconMapping.toState(emptyMap()) + } + override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup> = buildState { + unwrapped.defaultMobileIconGroup.toState(TelephonyIcons.THREE_G) + } + override val isDeviceEmergencyCallCapable: State<Boolean> = buildState { + unwrapped.isDeviceEmergencyCallCapable.toState() + } + override val isAnySimSecure: State<Boolean> = buildState { + unwrapped.isDeviceEmergencyCallCapable.toState() + } + override val isInEcmMode: State<Boolean> = stateOf(false) + } + + private class MobileConnectionRepoWrapper( + override val subId: Int, + override val carrierId: State<Int>, + override val inflateSignalStrength: State<Boolean>, + override val allowNetworkSliceIndicator: State<Boolean>, + override val tableLogBuffer: TableLogBuffer, + override val isEmergencyOnly: State<Boolean>, + override val isRoaming: State<Boolean>, + override val operatorAlphaShort: State<String?>, + override val isInService: State<Boolean>, + override val isNonTerrestrial: State<Boolean>, + override val isGsm: State<Boolean>, + override val cdmaLevel: State<Int>, + override val primaryLevel: State<Int>, + override val satelliteLevel: State<Int>, + override val dataConnectionState: State<DataConnectionState>, + override val dataActivityDirection: State<DataActivityModel>, + override val carrierNetworkChangeActive: State<Boolean>, + override val resolvedNetworkType: State<ResolvedNetworkType>, + override val numberOfLevels: State<Int>, + override val dataEnabled: State<Boolean>, + override val cdmaRoaming: State<Boolean>, + override val networkName: State<NetworkNameModel>, + override val carrierName: State<NetworkNameModel>, + override val isAllowedDuringAirplaneMode: State<Boolean>, + override val hasPrioritizedNetworkCapabilities: State<Boolean>, + override val isInEcmMode: State<Boolean>, + ) : MobileConnectionRepositoryKairos + + companion object { + /** Allows us to wrap a (likely fake) MobileConnectionRepository into a Kairos version. */ + fun BuildScope.wrapRepo( + conn: MobileConnectionRepository + ): MobileConnectionRepositoryKairos = + with(conn) { + MobileConnectionRepoWrapper( + subId = subId, + carrierId = carrierId.toState(), + inflateSignalStrength = inflateSignalStrength.toState(), + allowNetworkSliceIndicator = allowNetworkSliceIndicator.toState(), + tableLogBuffer = tableLogBuffer, + isEmergencyOnly = isEmergencyOnly.toState(), + isRoaming = isRoaming.toState(), + operatorAlphaShort = operatorAlphaShort.toState(), + isInService = isInService.toState(), + isNonTerrestrial = isNonTerrestrial.toState(), + isGsm = isGsm.toState(), + cdmaLevel = cdmaLevel.toState(), + primaryLevel = primaryLevel.toState(), + satelliteLevel = satelliteLevel.toState(), + dataConnectionState = dataConnectionState.toState(), + dataActivityDirection = dataActivityDirection.toState(), + carrierNetworkChangeActive = carrierNetworkChangeActive.toState(), + resolvedNetworkType = resolvedNetworkType.toState(), + numberOfLevels = numberOfLevels.toState(), + dataEnabled = dataEnabled.toState(), + cdmaRoaming = cdmaRoaming.toState(), + networkName = networkName.toState(), + carrierName = carrierName.toState(), + isAllowedDuringAirplaneMode = isAllowedDuringAirplaneMode.toState(), + hasPrioritizedNetworkCapabilities = hasPrioritizedNetworkCapabilities.toState(), + isInEcmMode = stateOf(false), + ) + } + } +} 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 9e914ad0a660..356e5676954e 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 @@ -58,8 +58,35 @@ import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) -class MobileIconsInteractorTest : SysuiTestCase() { - private val kosmos by lazy { +class MobileIconsInteractorTest : MobileIconsInteractorTestBase() { + override fun Kosmos.createInteractor() = + MobileIconsInteractorImpl( + mobileConnectionsRepository, + carrierConfigTracker, + tableLogger = mock(), + connectivityRepository, + FakeUserSetupRepository(), + testScope.backgroundScope, + context, + featureFlagsClassic, + ) + + @Test + fun iconInteractor_cachedPerSubId() = + kosmos.runTest { + connectionsRepository.setSubscriptions(listOf(SUB_1)) + runCurrent() + + val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID) + val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID) + + assertThat(interactor1).isNotNull() + assertThat(interactor1).isSameInstanceAs(interactor2) + } +} + +abstract class MobileIconsInteractorTestBase : SysuiTestCase() { + protected val kosmos by lazy { testKosmos().apply { mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest" mobileConnectionsRepository.fake.run { @@ -78,22 +105,13 @@ class MobileIconsInteractorTest : SysuiTestCase() { } // shortcut rename - private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake } + protected val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake } - private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() } + protected val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() } - private val Kosmos.underTest by Fixture { - MobileIconsInteractorImpl( - mobileConnectionsRepository, - carrierConfigTracker, - tableLogger = mock(), - connectivityRepository, - FakeUserSetupRepository(), - testScope.backgroundScope, - context, - featureFlagsClassic, - ) - } + protected val Kosmos.underTest by Fixture { createInteractor() } + + abstract fun Kosmos.createInteractor(): MobileIconsInteractor @Test fun filteredSubscriptions_default() = @@ -744,12 +762,15 @@ class MobileIconsInteractorTest : SysuiTestCase() { val latest by collectLastValue(underTest.mobileIsDefault) connectionsRepository.mobileIsDefault.value = true + runCurrent() assertThat(latest).isTrue() connectionsRepository.mobileIsDefault.value = false + runCurrent() assertThat(latest).isFalse() connectionsRepository.hasCarrierMergedConnection.value = true + runCurrent() assertThat(latest).isTrue() } @@ -874,16 +895,6 @@ class MobileIconsInteractorTest : SysuiTestCase() { } @Test - fun iconInteractor_cachedPerSubId() = - kosmos.runTest { - val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID) - val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID) - - assertThat(interactor1).isNotNull() - assertThat(interactor1).isSameInstanceAs(interactor2) - } - - @Test fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() = kosmos.runTest { val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode) @@ -1007,8 +1018,8 @@ class MobileIconsInteractorTest : SysuiTestCase() { companion object { - private const val SUB_1_ID = 1 - private val SUB_1 = + const val SUB_1_ID = 1 + val SUB_1 = SubscriptionModel( subscriptionId = SUB_1_ID, carrierName = "Carrier $SUB_1_ID", diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt index 6d3813c90bfd..bcc6ea3d3b35 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt @@ -20,8 +20,6 @@ import android.view.View import android.view.WindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.SysuiTestCase import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository import com.android.systemui.keyevent.data.repository.keyEventRepository @@ -58,20 +56,13 @@ class TopLevelWindowEffectsTest : SysuiTestCase() { private lateinit var windowManager: WindowManager @Mock - private lateinit var viewCapture: Lazy<ViewCapture> - - @Mock private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory private val Kosmos.underTest by Kosmos.Fixture { TopLevelWindowEffects( context = mContext, applicationScope = testScope.backgroundScope, - windowManager = ViewCaptureAwareWindowManager( - windowManager = windowManager, - lazyViewCapture = viewCapture, - isViewCaptureEnabled = false - ), + windowManager = windowManager, keyEventInteractor = keyEventInteractor, viewModelFactory = viewModelFactory, squeezeEffectInteractor = SqueezeEffectInteractor( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt index a303da0dae1a..49c06bf77fb6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt @@ -143,6 +143,7 @@ class VolumeDialogVisibilityInteractorTest : SysuiTestCase() { assertThat(visibilityModel) .isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + assertThat(fakeVolumeDialogController.hasUserActivity).isTrue() } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt new file mode 100644 index 000000000000..0847281d9b42 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt @@ -0,0 +1,82 @@ +/* + * 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, 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 androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.accessibilityRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.volume.Events +import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor +import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel +import com.google.common.truth.Truth.assertThat +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.test.advanceTimeBy +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class VolumeDialogPluginViewModelTest : SysuiTestCase() { + + private val kosmos: Kosmos = testKosmos() + + private val underTest: VolumeDialogPluginViewModel by lazy { + kosmos.volumeDialogPluginViewModel + } + + @Before + fun setUp() = + with(kosmos) { + volumeDialogVisibilityRepository.updateVisibility { + VolumeDialogVisibilityModel.Visible(Events.SHOW_REASON_VOLUME_CHANGED, false, 0) + } + } + + @Test + fun safetyWarningAppears_timeoutReset() = + kosmos.runTest { + accessibilityRepository.setRecommendedTimeout(3.seconds) + val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility) + testScope.advanceTimeBy(2.seconds) + assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + + underTest.onSafetyWarningDialogShown() + testScope.advanceTimeBy(2.seconds) + assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + } + + @Test + fun csdWarningAppears_timeoutReset() = + kosmos.runTest { + accessibilityRepository.setRecommendedTimeout(3.seconds) + val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility) + testScope.advanceTimeBy(2.seconds) + assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + + underTest.onCsdWarningDialogShown() + testScope.advanceTimeBy(2.seconds) + assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java index 60a15915fb77..046b5d701ddf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java @@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import org.junit.Before; import org.junit.Test; @@ -89,6 +90,8 @@ public class ImageWallpaperTest extends SysuiTestCase { private Context mMockContext; @Mock private UserTracker mUserTracker; + @Mock + private WindowManagerProvider mWindowManagerProvider; @Mock private Bitmap mWallpaperBitmap; @@ -105,7 +108,7 @@ public class ImageWallpaperTest extends SysuiTestCase { when(mWindowMetrics.getBounds()).thenReturn( new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics); - when(mMockContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); + when(mWindowManagerProvider.getWindowManager(any())).thenReturn(mWindowManager); // set up display manager doNothing().when(mDisplayManager).registerDisplayListener(any(), any()); @@ -182,7 +185,7 @@ public class ImageWallpaperTest extends SysuiTestCase { } private ImageWallpaper createImageWallpaper() { - return new ImageWallpaper(mFakeExecutor, mUserTracker) { + return new ImageWallpaper(mFakeExecutor, mUserTracker, mWindowManagerProvider) { @Override public Engine onCreateEngine() { return new CanvasEngine() { diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml index 43808f215a81..4002f7808637 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml @@ -21,6 +21,9 @@ style="@style/AuthCredentialPanelStyle" android:layout_width="0dp" android:layout_height="0dp" + android:accessibilityLiveRegion="assertive" + android:importantForAccessibility="yes" + android:clickable="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/rightGuideline" app:layout_constraintStart_toStartOf="@id/leftGuideline" diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml index 51117a7845df..3c8cb6860a41 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml @@ -22,6 +22,9 @@ android:layout_height="match_parent"> style="@style/AuthCredentialPanelStyle" android:layout_width="0dp" android:layout_height="0dp" + android:accessibilityLiveRegion="assertive" + android:importantForAccessibility="yes" + android:clickable="false" android:paddingHorizontal="16dp" android:paddingVertical="16dp" android:visibility="visible" diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index a1fa54cf592d..e094155f9041 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -16,17 +16,18 @@ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/volume_dialog" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="match_parent" android:alpha="0" - android:clipChildren="false"> + android:clipChildren="false" + android:minWidth="@dimen/volume_dialog_window_width"> <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_top_margin" - android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin" + android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative" android:background="@drawable/volume_dialog_background" app:layout_constraintBottom_toBottomOf="@id/volume_dialog_bottom_section_container" app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container" diff --git a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml index db800aa4a873..1dba96cafb70 100644 --- a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml +++ b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml @@ -17,8 +17,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_floating_slider_background" - android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_horizontal_padding" - android:paddingVertical="@dimen/volume_dialog_floating_sliders_vertical_padding"> + android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_padding" + android:paddingVertical="@dimen/volume_dialog_floating_sliders_padding"> <include layout="@layout/volume_dialog_slider" /> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml index b7455471d9a6..9d3598800bbf 100644 --- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml +++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml @@ -22,15 +22,15 @@ android:clipChildren="false" android:clipToPadding="false" android:gravity="center" - android:paddingEnd="@dimen/volume_dialog_buttons_margin" + android:paddingEnd="@dimen/volume_dialog_background_margin" app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene"> <View android:id="@+id/ringer_buttons_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:layout_marginTop="@dimen/volume_dialog_background_margin_negative" + android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative" android:background="@drawable/volume_dialog_ringer_background" android:visibility="gone" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7d983068f34e..55e94028b95e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -2158,28 +2158,25 @@ <dimen name="contextual_edu_dialog_elevation">2dp</dimen> <!-- Volume start --> + <dimen name="volume_dialog_window_width">176dp</dimen> <dimen name="volume_dialog_width">60dp</dimen> <dimen name="volume_dialog_background_corner_radius">30dp</dimen> - <dimen name="volume_dialog_background_vertical_margin"> - @dimen/volume_dialog_buttons_margin_negative - </dimen> <!-- top margin covers half the ringer button + components spacing --> <dimen name="volume_dialog_background_top_margin">-28dp</dimen> + <dimen name="volume_dialog_background_margin">10dp</dimen> + <dimen name="volume_dialog_background_margin_negative">-10dp</dimen> - <dimen name="volume_dialog_window_margin">14dp</dimen> + <dimen name="volume_dialog_window_margin">12dp</dimen> <dimen name="volume_dialog_components_spacing">10dp</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"> - @dimen/volume_dialog_buttons_margin_negative + @dimen/volume_dialog_background_margin_negative </dimen> - <dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen> + <dimen name="volume_dialog_floating_sliders_padding">4dp</dimen> <dimen name="volume_dialog_button_size">40dp</dimen> <dimen name="volume_dialog_slider_width">52dp</dimen> <dimen name="volume_dialog_slider_height">254dp</dimen> - <dimen name="volume_dialog_buttons_margin">10dp</dimen> - <dimen name="volume_dialog_buttons_margin_negative">-10dp</dimen> <!-- A primary goal of this margin is to vertically constraint slider height in the landscape orientation when the vertical space is limited @@ -2190,8 +2187,10 @@ <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen> - <dimen name="volume_dialog_ringer_drawer_margin">@dimen/volume_dialog_buttons_margin</dimen> <dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen> + <dimen name="volume_dialog_ringer_drawer_buttons_spacing"> + @dimen/volume_dialog_components_spacing + </dimen> <dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen> <dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen> diff --git a/packages/SystemUI/shared/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp index 63de81d4a680..208157c69adf 100644 --- a/packages/SystemUI/shared/biometrics/Android.bp +++ b/packages/SystemUI/shared/biometrics/Android.bp @@ -14,6 +14,9 @@ android_library { "src/**/*.java", "src/**/*.kt", ], + static_libs: [ + "SystemUI-shared-utils", + ], resource_dirs: [ "res", ], diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt index 5b99a3f16fc2..7aa09cf64405 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt @@ -41,6 +41,7 @@ import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.utils.windowmanager.WindowManagerUtils object Utils { private const val TAG = "SysUIBiometricUtils" @@ -117,10 +118,9 @@ object Utils { @JvmStatic fun getNavbarInsets(context: Context): Insets { - val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java) - val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics - return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars()) - ?: Insets.NONE + val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context) + val windowMetrics: WindowMetrics = windowManager.maximumWindowMetrics + return windowMetrics.windowInsets.getInsets(WindowInsets.Type.navigationBars()) } /** Converts `drawable` to a [Bitmap]. */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index c82243934b8b..b1fc560f406b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -37,6 +37,7 @@ import android.view.Surface; import android.view.WindowManager; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; /* Common code */ public class Utilities { @@ -152,7 +153,7 @@ public class Utilities { /** @return whether or not {@param context} represents that of a large screen device or not */ @TargetApi(Build.VERSION_CODES.R) public static boolean isLargeScreen(Context context) { - return isLargeScreen(context.getSystemService(WindowManager.class), context.getResources()); + return isLargeScreen(WindowManagerUtils.getWindowManager(context), context.getResources()); } /** @return whether or not {@param context} represents that of a large screen device or not */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java index 4db6ab6ea579..570d774b95e9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java @@ -16,9 +16,6 @@ package com.android.systemui.shared.rotation; -import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance; -import static com.android.systemui.Flags.enableViewCaptureTracing; - import android.annotation.DimenRes; import android.annotation.IdRes; import android.annotation.LayoutRes; @@ -33,6 +30,7 @@ import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; @@ -40,8 +38,8 @@ import android.widget.FrameLayout; import androidx.annotation.BoolRes; import androidx.core.view.OneShotPreDrawListener; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; /** * Containing logic for the rotation button on the physical left bottom corner of the screen. @@ -50,7 +48,7 @@ public class FloatingRotationButton implements RotationButton { private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300; - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final ViewGroup mKeyButtonContainer; private final FloatingRotationButtonView mKeyButtonView; @@ -91,8 +89,7 @@ public class FloatingRotationButton implements RotationButton { @DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter, @DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) { mContext = context; - mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext, - enableViewCaptureTracing()); + mWindowManager = WindowManagerUtils.getWindowManager(mContext); mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null); mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId); mKeyButtonView.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 19da5de6b531..59ec6923ce91 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -66,7 +66,6 @@ import android.widget.FrameLayout; import androidx.annotation.VisibleForTesting; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.util.Preconditions; import com.android.settingslib.Utils; import com.android.systemui.biometrics.data.repository.FacePropertyRepository; @@ -167,7 +166,7 @@ public class ScreenDecorations implements ViewGroup mScreenDecorHwcWindow; @VisibleForTesting ScreenDecorHwcLayer mScreenDecorHwcLayer; - private ViewCaptureAwareWindowManager mWindowManager; + private WindowManager mWindowManager; private int mRotation; private UserSettingObserver mColorInversionSetting; private DelayableExecutor mExecutor; @@ -337,7 +336,7 @@ public class ScreenDecorations implements FacePropertyRepository facePropertyRepository, JavaAdapter javaAdapter, CameraProtectionLoader cameraProtectionLoader, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + WindowManager windowManager, @ScreenDecorationsThread Handler handler, @ScreenDecorationsThread DelayableExecutor executor) { mContext = context; @@ -353,7 +352,7 @@ public class ScreenDecorations implements mLogger = logger; mFacePropertyRepository = facePropertyRepository; mJavaAdapter = javaAdapter; - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mHandler = handler; mExecutor = executor; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java index 115242eb13aa..375137c67f7c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java @@ -44,7 +44,6 @@ import android.window.InputTransferToken; import androidx.annotation.NonNull; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.dagger.SysUISingleton; @@ -54,6 +53,7 @@ import com.android.systemui.recents.LauncherProxyService; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -97,20 +97,19 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks private final WindowMagnifierCallback mWindowMagnifierCallback; private final SysUiState mSysUiState; private final SecureSettings mSecureSettings; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private final WindowManagerProvider mWindowManagerProvider; WindowMagnificationControllerSupplier(Context context, Handler handler, WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager, SysUiState sysUiState, - SecureSettings secureSettings, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) { super(displayManager); mContext = context; mHandler = handler; mWindowMagnifierCallback = windowMagnifierCallback; mSysUiState = sysUiState; mSecureSettings = secureSettings; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManagerProvider = windowManagerProvider; } @Override @@ -118,6 +117,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks final Context windowContext = mContext.createWindowContext(display, TYPE_ACCESSIBILITY_OVERLAY, /* options */ null); + final WindowManager windowManager = mWindowManagerProvider + .getWindowManager(windowContext); windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); Supplier<SurfaceControlViewHost> scvhSupplier = () -> @@ -133,7 +134,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks mWindowMagnifierCallback, mSysUiState, mSecureSettings, - scvhSupplier); + scvhSupplier, + windowManager); } } @@ -148,17 +150,20 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks private final Executor mExecutor; private final DisplayManager mDisplayManager; private final IWindowManager mIWindowManager; + private final WindowManagerProvider mWindowManagerProvider; FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager, Handler handler, - Executor executor, IWindowManager iWindowManager) { + Executor executor, IWindowManager iWindowManager, + WindowManagerProvider windowManagerProvider) { super(displayManager); mContext = context; mHandler = handler; mExecutor = executor; mDisplayManager = displayManager; mIWindowManager = iWindowManager; + mWindowManagerProvider = windowManagerProvider; } @Override @@ -174,7 +179,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks mExecutor, mDisplayManager, windowContext.getSystemService(AccessibilityManager.class), - windowContext.getSystemService(WindowManager.class), + mWindowManagerProvider.getWindowManager(windowContext), mIWindowManager, scvhSupplier); } @@ -190,31 +195,32 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks private final Context mContext; private final MagnificationSettingsController.Callback mSettingsControllerCallback; private final SecureSettings mSecureSettings; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private final WindowManagerProvider mWindowManagerProvider; SettingsSupplier(Context context, MagnificationSettingsController.Callback settingsControllerCallback, DisplayManager displayManager, - SecureSettings secureSettings, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) { super(displayManager); mContext = context; mSettingsControllerCallback = settingsControllerCallback; mSecureSettings = secureSettings; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManagerProvider = windowManagerProvider; } @Override protected MagnificationSettingsController createInstance(Display display) { final Context windowContext = mContext.createWindowContext(display, TYPE_ACCESSIBILITY_OVERLAY, /* options */ null); + final WindowManager windowManager = mWindowManagerProvider + .getWindowManager(windowContext); windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); return new MagnificationSettingsController( windowContext, new SfVsyncFrameCallbackProvider(), mSettingsControllerCallback, mSecureSettings, - mViewCaptureAwareWindowManager); + windowManager); } } @@ -229,11 +235,11 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks SecureSettings secureSettings, DisplayTracker displayTracker, DisplayManager displayManager, AccessibilityLogger a11yLogger, IWindowManager iWindowManager, AccessibilityManager accessibilityManager, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManagerProvider windowManagerProvider) { this(context, mainHandler.getLooper(), executor, commandQueue, modeSwitchesController, sysUiState, launcherProxyService, secureSettings, displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager, - viewCaptureAwareWindowManager); + windowManagerProvider); } @VisibleForTesting @@ -244,7 +250,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks DisplayManager displayManager, AccessibilityLogger a11yLogger, IWindowManager iWindowManager, AccessibilityManager accessibilityManager, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManagerProvider windowManagerProvider) { mHandler = new Handler(looper) { @Override public void handleMessage(@NonNull Message msg) { @@ -263,12 +269,13 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks mA11yLogger = a11yLogger; mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context, mHandler, mWindowMagnifierCallback, - displayManager, sysUiState, secureSettings, viewCaptureAwareWindowManager); + displayManager, sysUiState, secureSettings, windowManagerProvider); mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier( - context, displayManager, mHandler, mExecutor, iWindowManager); + context, displayManager, mHandler, mExecutor, iWindowManager, + windowManagerProvider); mMagnificationSettingsSupplier = new SettingsSupplier(context, mMagnificationSettingsControllerCallback, displayManager, secureSettings, - viewCaptureAwareWindowManager); + windowManagerProvider); mModeSwitchesController.setClickListenerDelegate( displayId -> mHandler.post(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index 4723ab958f86..9eb01de239bc 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -46,7 +46,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.ImageView; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.res.R; @@ -77,7 +76,6 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL private final Context mContext; private final AccessibilityManager mAccessibilityManager; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final ImageView mImageView; private final Runnable mWindowInsetChangeRunnable; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @@ -101,21 +99,20 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL void onClick(int displayId); } - MagnificationModeSwitch(@UiContext Context context, ClickListener clickListener, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { - this(context, createView(context), new SfVsyncFrameCallbackProvider(), clickListener, - viewCaptureAwareWindowManager); + MagnificationModeSwitch(@UiContext Context context, WindowManager windowManager, + ClickListener clickListener) { + this(context, windowManager, createView(context), new SfVsyncFrameCallbackProvider(), + clickListener); } @VisibleForTesting - MagnificationModeSwitch(Context context, @NonNull ImageView imageView, - SfVsyncFrameCallbackProvider sfVsyncFrameProvider, ClickListener clickListener, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + MagnificationModeSwitch(Context context, WindowManager windowManager, + @NonNull ImageView imageView, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, + ClickListener clickListener) { mContext = context; mConfiguration = new Configuration(context.getResources().getConfiguration()); mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); - mWindowManager = mContext.getSystemService(WindowManager.class); - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mSfVsyncFrameProvider = sfVsyncFrameProvider; mClickListener = clickListener; mParams = createLayoutParams(context); @@ -282,7 +279,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL mImageView.animate().cancel(); mIsFadeOutAnimating = false; mImageView.setAlpha(0f); - mViewCaptureAwareWindowManager.removeView(mImageView); + mWindowManager.removeView(mImageView); mContext.unregisterComponentCallbacks(this); mIsVisible = false; } @@ -316,7 +313,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL mParams.y = mDraggableWindowBounds.bottom; mToLeftScreenEdge = false; } - mViewCaptureAwareWindowManager.addView(mImageView, mParams); + mWindowManager.addView(mImageView, mParams); // Exclude magnification switch button from system gesture area. setSystemGestureExclusion(); mIsVisible = true; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java index fc7535a712e3..2d5dc8d23383 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java @@ -26,7 +26,6 @@ import android.content.res.Configuration; import android.util.Range; import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.accessibility.common.MagnificationConstants; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -62,9 +61,9 @@ public class MagnificationSettingsController implements ComponentCallbacks { SfVsyncFrameCallbackProvider sfVsyncFrameProvider, @NonNull Callback settingsControllerCallback, SecureSettings secureSettings, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { - this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings, null, - viewCaptureAwareWindowManager); + WindowManager windowManager) { + this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings, + windowManager, null); } @VisibleForTesting @@ -73,8 +72,8 @@ public class MagnificationSettingsController implements ComponentCallbacks { SfVsyncFrameCallbackProvider sfVsyncFrameProvider, @NonNull Callback settingsControllerCallback, SecureSettings secureSettings, - WindowMagnificationSettings windowMagnificationSettings, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager, + WindowMagnificationSettings windowMagnificationSettings) { mContext = context.createWindowContext( context.getDisplay(), WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, @@ -88,7 +87,7 @@ public class MagnificationSettingsController implements ComponentCallbacks { } else { mWindowMagnificationSettings = new WindowMagnificationSettings(mContext, mWindowMagnificationSettingsCallback, - sfVsyncFrameProvider, secureSettings, viewCaptureAwareWindowManager); + sfVsyncFrameProvider, secureSettings, windowManager); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java index eb4de6837d41..7f3a869d8222 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java @@ -18,9 +18,6 @@ package com.android.systemui.accessibility; import static android.view.WindowManager.LayoutParams; -import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance; -import static com.android.systemui.Flags.enableViewCaptureTracing; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -32,8 +29,8 @@ import android.util.MathUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.res.R; /** @@ -73,12 +70,11 @@ public abstract class MirrorWindowControl { * @see #setDefaultPosition(LayoutParams) */ private final Point mControlPosition = new Point(); - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; - MirrorWindowControl(Context context) { + MirrorWindowControl(Context context, WindowManager windowManager) { mContext = context; - mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext, - enableViewCaptureTracing()); + mWindowManager = windowManager; } public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java index 53827e65344a..7d9f8674457c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java @@ -24,10 +24,11 @@ import android.annotation.MainThread; import android.content.Context; import android.hardware.display.DisplayManager; import android.view.Display; +import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import javax.inject.Inject; @@ -49,9 +50,9 @@ public class ModeSwitchesController implements ClickListener { @Inject public ModeSwitchesController(Context context, DisplayManager displayManager, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManagerProvider windowManagerProvider) { mSwitchSupplier = new SwitchSupplier(context, displayManager, this::onClick, - viewCaptureAwareWindowManager); + windowManagerProvider); } @VisibleForTesting @@ -118,7 +119,7 @@ public class ModeSwitchesController implements ClickListener { private final Context mContext; private final ClickListener mClickListener; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private final WindowManagerProvider mWindowManagerProvider; /** * Supplies the switch for the given display. @@ -128,20 +129,20 @@ public class ModeSwitchesController implements ClickListener { * @param clickListener The callback that will run when the switch is clicked */ SwitchSupplier(Context context, DisplayManager displayManager, - ClickListener clickListener, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + ClickListener clickListener, WindowManagerProvider windowManagerProvider) { super(displayManager); mContext = context; mClickListener = clickListener; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManagerProvider = windowManagerProvider; } @Override protected MagnificationModeSwitch createInstance(Display display) { final Context uiContext = mContext.createWindowContext(display, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); - return new MagnificationModeSwitch(uiContext, mClickListener, - mViewCaptureAwareWindowManager); + final WindowManager uiWindowManager = mWindowManagerProvider + .getWindowManager(uiContext); + return new MagnificationModeSwitch(uiContext, uiWindowManager, mClickListener); } } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java index bc469eed7359..3cde033bf56a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java @@ -27,6 +27,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.WindowManager; import com.android.systemui.res.R; @@ -48,8 +49,8 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl private final PointF mLastDrag = new PointF(); private final Handler mHandler; - SimpleMirrorWindowControl(Context context, Handler handler) { - super(context); + SimpleMirrorWindowControl(Context context, Handler handler, WindowManager windowManager) { + super(context, windowManager); mHandler = handler; final Resources resource = context.getResources(); mMoveFrameAmountShort = resource.getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 8734d05bc894..9cd77e790e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -249,7 +249,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold @NonNull WindowMagnifierCallback callback, SysUiState sysUiState, SecureSettings secureSettings, - Supplier<SurfaceControlViewHost> scvhSupplier) { + Supplier<SurfaceControlViewHost> scvhSupplier, + WindowManager windowManager) { mContext = context; mHandler = handler; mAnimationController = animationController; @@ -265,7 +266,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mDisplayId = mContext.getDisplayId(); mRotation = display.getRotation(); - mWm = context.getSystemService(WindowManager.class); + mWm = windowManager; mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds()); mResources = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 3b6f8f87a1a8..bd4b6420bf37 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -56,7 +56,6 @@ import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.Switch; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; @@ -75,7 +74,6 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest private final Context mContext; private final AccessibilityManager mAccessibilityManager; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final SecureSettings mSecureSettings; private final Runnable mWindowInsetChangeRunnable; @@ -137,11 +135,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest @VisibleForTesting WindowMagnificationSettings(Context context, WindowMagnificationSettingsCallback callback, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SecureSettings secureSettings, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager) { mContext = context; mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); - mWindowManager = mContext.getSystemService(WindowManager.class); - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mSfVsyncFrameProvider = sfVsyncFrameProvider; mCallback = callback; mSecureSettings = secureSettings; @@ -324,7 +321,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest // Unregister observer before removing view mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver); - mViewCaptureAwareWindowManager.removeView(mSettingView); + mWindowManager.removeView(mSettingView); mIsVisible = false; if (resetPosition) { mParams.x = 0; @@ -382,7 +379,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mParams.y = mDraggableWindowBounds.bottom; } - mViewCaptureAwareWindowManager.addView(mSettingView, mParams); + mWindowManager.addView(mSettingView, mParams); mSecureSettings.registerContentObserverForUserSync( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index f8e4bda15d01..ef42837ba776 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -31,7 +31,6 @@ import android.view.accessibility.IUserInitializationCompleteCallback; import androidx.annotation.MainThread; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -60,7 +59,6 @@ public class AccessibilityFloatingMenuController implements private final Context mContext; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final DisplayManager mDisplayManager; private final AccessibilityManager mAccessibilityManager; private final HearingAidDeviceManager mHearingAidDeviceManager; @@ -105,7 +103,6 @@ public class AccessibilityFloatingMenuController implements @Inject public AccessibilityFloatingMenuController(Context context, WindowManager windowManager, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, DisplayManager displayManager, AccessibilityManager accessibilityManager, AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, @@ -118,7 +115,6 @@ public class AccessibilityFloatingMenuController implements @Main Handler handler) { mContext = context; mWindowManager = windowManager; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; mDisplayManager = displayManager; mAccessibilityManager = accessibilityManager; mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver; @@ -205,8 +201,8 @@ public class AccessibilityFloatingMenuController implements final Context windowContext = mContext.createWindowContext(defaultDisplay, TYPE_NAVIGATION_BAR_PANEL, /* options= */ null); mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager, - mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController, mHearingAidDeviceManager); + mAccessibilityManager, mSecureSettings, mNavigationModeController, + mHearingAidDeviceManager); } mFloatingMenu.show(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt index 13c1a450832f..52e69efdbc19 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt @@ -48,7 +48,7 @@ import com.android.wm.shell.shared.bubbles.DismissView * * @note [setup] method should be called after initialisation */ -class DragToInteractView(context: Context) : FrameLayout(context) { +class DragToInteractView(context: Context, windowManager: WindowManager) : FrameLayout(context) { /** * The configuration is used to provide module specific resource ids * @@ -86,8 +86,7 @@ class DragToInteractView(context: Context) : FrameLayout(context) { private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY) private val INTERACT_SCRIM_FADE_MS = 200L - private var wm: WindowManager = - context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + private var wm: WindowManager = windowManager private var gradientDrawable: GradientDrawable? = null private val GRADIENT_ALPHA: IntProperty<GradientDrawable> = diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 81095220b4a6..8fb260c2df36 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -212,7 +212,7 @@ class MenuViewLayer extends FrameLayout implements mMenuAnimationController = mMenuView.getMenuAnimationController(); mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction); mDismissView = new DismissView(context); - mDragToInteractView = new DragToInteractView(context); + mDragToInteractView = new DragToInteractView(context, windowManager); DismissViewUtils.setup(mDismissView); mDismissView.getCircle().setId(R.id.action_remove_menu); mNotificationFactory = new MenuNotificationFactory(context); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java index 102efcf7badd..7bf7e23b5df5 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java @@ -25,7 +25,6 @@ import android.graphics.PixelFormat; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.util.settings.SecureSettings; @@ -35,16 +34,15 @@ import com.android.systemui.util.settings.SecureSettings; * of {@link IAccessibilityFloatingMenu}. */ class MenuViewLayerController implements IAccessibilityFloatingMenu { - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final MenuViewLayer mMenuViewLayer; private boolean mIsShowing; MenuViewLayerController(Context context, WindowManager windowManager, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, AccessibilityManager accessibilityManager, SecureSettings secureSettings, NavigationModeController navigationModeController, HearingAidDeviceManager hearingAidDeviceManager) { - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; MenuViewModel menuViewModel = new MenuViewModel( context, accessibilityManager, secureSettings, hearingAidDeviceManager); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java index f4e2b82f1773..14ce2cf60919 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java @@ -33,7 +33,6 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import com.android.app.animation.Interpolators; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.res.R; /** @@ -41,17 +40,16 @@ import com.android.systemui.res.R; */ public class AssistDisclosure { private final Context mContext; - private final ViewCaptureAwareWindowManager mWm; + private final WindowManager mWm; private final Handler mHandler; private AssistDisclosureView mView; private boolean mViewAdded; - public AssistDisclosure(Context context, Handler handler, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + public AssistDisclosure(Context context, Handler handler, WindowManager windowManager) { mContext = context; mHandler = handler; - mWm = viewCaptureAwareWindowManager; + mWm = windowManager; } public void postShow() { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 2d44d401b0b0..75ec8dfd881e 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -24,8 +24,8 @@ import android.provider.Settings; import android.service.voice.VisualQueryAttentionResult; import android.service.voice.VoiceInteractionSession; import android.util.Log; +import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.internal.app.IVisualQueryRecognitionStatusListener; @@ -199,12 +199,12 @@ public class AssistManager { SelectedUserInteractor selectedUserInteractor, ActivityManager activityManager, AssistInteractor interactor, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager) { mContext = context; mDeviceProvisionedController = controller; mCommandQueue = commandQueue; mAssistUtils = assistUtils; - mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager); + mAssistDisclosure = new AssistDisclosure(context, uiHandler, windowManager); mLauncherProxyService = launcherProxyService; mPhoneStateMonitor = phoneStateMonitor; mAssistLogger = assistLogger; diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java index 6e257442d139..56273eb9a2cf 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java @@ -33,7 +33,6 @@ import android.view.WindowManager; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.assist.AssistLogger; @@ -67,7 +66,7 @@ public class DefaultUiController implements AssistManager.UiController { protected InvocationLightsView mInvocationLightsView; protected final AssistLogger mAssistLogger; - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final MetricsLogger mMetricsLogger; private final Lazy<AssistManager> mAssistManagerLazy; private final WindowManager.LayoutParams mLayoutParams; @@ -81,12 +80,12 @@ public class DefaultUiController implements AssistManager.UiController { @Inject public DefaultUiController(Context context, AssistLogger assistLogger, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, - MetricsLogger metricsLogger, Lazy<AssistManager> assistManagerLazy, + WindowManager windowManager, MetricsLogger metricsLogger, + Lazy<AssistManager> assistManagerLazy, NavigationBarController navigationBarController) { mAssistLogger = assistLogger; mRoot = new FrameLayout(context); - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mMetricsLogger = metricsLogger; mAssistManagerLazy = assistManagerLazy; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b8e95ee1dbf0..7bb08edd4773 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -20,7 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; -import static com.android.systemui.Flags.enableViewCaptureTracing; import android.animation.Animator; import android.annotation.IntDef; @@ -57,8 +56,6 @@ import android.window.OnBackInvokedDispatcher; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.app.animation.Interpolators; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; @@ -78,11 +75,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.res.R; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; import com.google.android.msdl.domain.MSDLPlayer; -import kotlin.Lazy; - import kotlinx.coroutines.CoroutineScope; import java.io.PrintWriter; @@ -132,7 +128,7 @@ public class AuthContainerView extends LinearLayout private final Config mConfig; private final int mEffectiveUserId; private final IBinder mWindowToken = new Binder(); - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; @Nullable private final AuthContextPlugins mAuthContextPlugins; private final Interpolator mLinearOutSlowIn; private final LockPatternUtils mLockPatternUtils; @@ -298,16 +294,13 @@ public class AuthContainerView extends LinearLayout @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull @Background DelayableExecutor bgExecutor, @NonNull VibratorHelper vibratorHelper, - Lazy<ViewCapture> lazyViewCapture, @NonNull MSDLPlayer msdlPlayer) { super(config.mContext); mConfig = config; mLockPatternUtils = lockPatternUtils; mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId); - WindowManager wm = getContext().getSystemService(WindowManager.class); - mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture, - enableViewCaptureTracing()); + mWindowManager = WindowManagerUtils.getWindowManager(getContext()); mAuthContextPlugins = authContextPlugins; mWakefulnessLifecycle = wakefulnessLifecycle; mApplicationCoroutineScope = applicationCoroutineScope; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 68a282018ba4..f2c071f14466 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -22,7 +22,6 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR import static android.view.Display.INVALID_DISPLAY; import static com.android.systemui.Flags.contAuthPlugin; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import android.annotation.NonNull; import android.annotation.Nullable; @@ -67,7 +66,6 @@ import android.view.DisplayInfo; import android.view.MotionEvent; import android.view.WindowManager; -import com.android.app.viewcapture.ViewCapture; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; @@ -93,6 +91,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.google.android.msdl.domain.MSDLPlayer; @@ -194,7 +193,7 @@ public class AuthController implements @NonNull private final VibratorHelper mVibratorHelper; @NonNull private final MSDLPlayer mMSDLPlayer; - private final kotlin.Lazy<ViewCapture> mLazyViewCapture; + private final WindowManagerProvider mWindowManagerProvider; @VisibleForTesting final TaskStackListener mTaskStackListener = new TaskStackListener() { @@ -693,8 +692,8 @@ public class AuthController implements @NonNull UdfpsUtils udfpsUtils, @NonNull VibratorHelper vibratorHelper, @NonNull KeyguardManager keyguardManager, - Lazy<ViewCapture> daggerLazyViewCapture, - @NonNull MSDLPlayer msdlPlayer) { + @NonNull MSDLPlayer msdlPlayer, + WindowManagerProvider windowManagerProvider) { mContext = context; mExecution = execution; mUserManager = userManager; @@ -755,7 +754,7 @@ public class AuthController implements context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED); mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); - mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture); + mWindowManagerProvider = windowManagerProvider; } // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. @@ -1278,7 +1277,7 @@ public class AuthController implements Log.e(TAG, "unable to get Display for user=" + userId); return null; } - return mContext.createDisplayContext(display).getSystemService(WindowManager.class); + return mWindowManagerProvider.getWindowManager(mContext.createDisplayContext(display)); } private void onDialogDismissed(@BiometricPrompt.DismissedReason int reason) { @@ -1337,8 +1336,7 @@ public class AuthController implements return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps, wakefulnessLifecycle, userManager, mContextPlugins, lockPatternUtils, mInteractionJankMonitor, mPromptSelectorInteractor, viewModel, - mCredentialViewModelProvider, bgExecutor, mVibratorHelper, - mLazyViewCapture, mMSDLPlayer); + mCredentialViewModelProvider, bgExecutor, mVibratorHelper, mMSDLPlayer); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 659d3b46fea9..b16c416fb9df 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -57,12 +57,12 @@ import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; @@ -145,7 +145,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { private final Execution mExecution; private final FingerprintManager mFingerprintManager; @NonNull private final LayoutInflater mInflater; - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final DelayableExecutor mFgExecutor; @NonNull private final Executor mBiometricExecutor; @NonNull private final StatusBarStateController mStatusBarStateController; @@ -659,7 +659,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull Execution execution, @NonNull @ShadeDisplayAware LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, - @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + @NonNull @Main WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, @@ -705,7 +705,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { // The fingerprint manager is queried for UDFPS before this class is constructed, so the // fingerprint manager should never be null. mFingerprintManager = checkNotNull(fingerprintManager); - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mFgExecutor = fgExecutor; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index bdf58275effa..61b670715572 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -42,7 +42,6 @@ import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import androidx.annotation.VisibleForTesting import com.android.app.tracing.coroutines.launchTraced as launch -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor @@ -88,7 +87,7 @@ class UdfpsControllerOverlay constructor( private val context: Context, private val inflater: LayoutInflater, - private val windowManager: ViewCaptureAwareWindowManager, + private val windowManager: WindowManager, private val accessibilityManager: AccessibilityManager, private val statusBarStateController: StatusBarStateController, private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 54c52b533da4..3b22e13f29a2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -397,7 +397,7 @@ object BiometricViewBinder { // Talkback directional guidance udfpsGuidanceView.setOnHoverListener { _, event -> launch { - viewModel.onAnnounceAccessibilityHint( + viewModel.onUpdateAccessibilityHint( event, accessibilityManager.isTouchExplorationEnabled, ) @@ -406,7 +406,9 @@ object BiometricViewBinder { } launch { viewModel.accessibilityHint.collect { message -> - if (message.isNotBlank()) view.announceForAccessibility(message) + if (message.isNotBlank()) { + udfpsGuidanceView.contentDescription = message + } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 02c378417f3d..fcc0121e8f93 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -27,7 +27,6 @@ import android.view.View import android.view.ViewGroup import android.view.ViewOutlineProvider import android.view.WindowInsets -import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.widget.ImageView import android.widget.TextView @@ -48,6 +47,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isSmall import com.android.systemui.biometrics.ui.viewmodel.isTop import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R +import com.android.systemui.utils.windowmanager.WindowManagerUtils import kotlin.math.abs import kotlinx.coroutines.flow.combine import com.android.app.tracing.coroutines.launchTraced as launch @@ -66,7 +66,7 @@ object BiometricViewSizeBinder { viewsToHideWhenSmall: List<View>, jankListener: BiometricJankListener, ) { - val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java)) + val windowManager = WindowManagerUtils.getWindowManager(view.context) val accessibilityManager = requireNotNull(view.context.getSystemService(AccessibilityManager::class.java)) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 0902d19b6787..4e17a2658ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -886,7 +886,7 @@ constructor( } /** Sets the message used for UDFPS directional guidance */ - suspend fun onAnnounceAccessibilityHint( + suspend fun onUpdateAccessibilityHint( event: MotionEvent, touchExplorationEnabled: Boolean, ): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt index 7d518f4f7e78..718ef51aa161 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt @@ -23,20 +23,19 @@ import android.os.SystemProperties import android.view.Surface import android.view.View import android.view.WindowManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.annotations.VisibleForTesting import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger import com.android.settingslib.Utils +import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.res.R +import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import javax.inject.Inject @@ -58,7 +57,6 @@ class WiredChargingRippleController @Inject constructor( featureFlags: FeatureFlags, private val context: Context, private val windowManager: WindowManager, - private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, private val systemClock: SystemClock, private val uiEventLogger: UiEventLogger ) { @@ -163,12 +161,12 @@ class WiredChargingRippleController @Inject constructor( override fun onViewAttachedToWindow(view: View) { layoutRipple() rippleView.startRipple(Runnable { - viewCaptureAwareWindowManager.removeView(rippleView) + windowManager.removeView(rippleView) }) rippleView.removeOnAttachStateChangeListener(this) } }) - viewCaptureAwareWindowManager.addView(rippleView, windowLayoutParams) + windowManager.addView(rippleView, windowLayoutParams) uiEventLogger.log(WiredChargingRippleEvent.CHARGING_RIPPLE_PLAYED) } diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java index e5e9c4685264..4ca55400561e 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java @@ -28,10 +28,10 @@ import android.util.Slog; import android.view.Gravity; import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; /** * A WirelessChargingAnimation is a view containing view + animation for wireless charging. @@ -60,11 +60,11 @@ public class WirelessChargingAnimation { */ private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, - RippleShape rippleShape, UiEventLogger uiEventLogger, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + RippleShape rippleShape, UiEventLogger uiEventLogger, WindowManager windowManager, + WindowManagerProvider windowManagerProvider) { mCurrentWirelessChargingView = new WirelessChargingView(context, looper, transmittingBatteryLevel, batteryLevel, callback, isDozing, - rippleShape, uiEventLogger, viewCaptureAwareWindowManager); + rippleShape, uiEventLogger, windowManager, windowManagerProvider); } /** @@ -75,11 +75,11 @@ public class WirelessChargingAnimation { public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, RippleShape rippleShape, - UiEventLogger uiEventLogger, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + UiEventLogger uiEventLogger, WindowManager windowManager, + WindowManagerProvider windowManagerProvider) { return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel, - batteryLevel, callback, isDozing, rippleShape, uiEventLogger, - viewCaptureAwareWindowManager); + batteryLevel, callback, isDozing, rippleShape, uiEventLogger, windowManager, + windowManagerProvider); } /** @@ -88,10 +88,10 @@ public class WirelessChargingAnimation { */ public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel( @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager, WindowManagerProvider windowManagerProvider) { return makeWirelessChargingAnimation(context, null, UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false, - rippleShape, uiEventLogger, viewCaptureAwareWindowManager); + rippleShape, uiEventLogger, windowManager, windowManagerProvider); } /** @@ -123,19 +123,21 @@ public class WirelessChargingAnimation { private int mGravity; private WirelessChargingLayout mView; private WirelessChargingLayout mNextView; - private ViewCaptureAwareWindowManager mWM; + private WindowManager mWM; private Callback mCallback; + private WindowManagerProvider mWindowManagerProvider; public WirelessChargingView(Context context, @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager, WindowManagerProvider windowManagerProvider) { mCallback = callback; mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape); mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER; mUiEventLogger = uiEventLogger; - mWM = viewCaptureAwareWindowManager; + mWM = windowManager; + mWindowManagerProvider = windowManagerProvider; final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.MATCH_PARENT; @@ -207,6 +209,7 @@ public class WirelessChargingAnimation { if (context == null) { context = mView.getContext(); } + mWM = mWindowManagerProvider.getWindowManager(context); mParams.packageName = packageName; mParams.hideTimeoutMilliseconds = DURATION; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java index dc3b50c93298..059ea3271235 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java @@ -27,7 +27,6 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.policy.PhoneWindow; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext; import com.android.systemui.screenshot.FloatingWindowUtil; @@ -45,7 +44,6 @@ public class ClipboardOverlayWindow extends PhoneWindow private final Context mContext; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final WindowManager.LayoutParams mWindowLayoutParams; private boolean mKeyboardVisible; @@ -55,7 +53,6 @@ public class ClipboardOverlayWindow extends PhoneWindow @Inject ClipboardOverlayWindow(@OverlayWindowContext Context context, - @OverlayWindowContext ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, @OverlayWindowContext WindowManager windowManager) { super(context); mContext = context; @@ -66,10 +63,9 @@ public class ClipboardOverlayWindow extends PhoneWindow requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); setBackgroundDrawableResource(android.R.color.transparent); mWindowManager = windowManager; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams(); mWindowLayoutParams.setTitle("ClipboardOverlay"); - setWindowManager(windowManager, null, null); + setWindowManager(mWindowManager, null, null); setWindowFocusable(false); } @@ -86,12 +82,10 @@ public class ClipboardOverlayWindow extends PhoneWindow attach(); withWindowAttached(() -> { - WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics() - .getWindowInsets(); + WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); mKeyboardVisible = currentInsets.isVisible(WindowInsets.Type.ime()); peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> { - WindowInsets insets = mWindowManager.getCurrentWindowMetrics() - .getWindowInsets(); + WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime()); if (keyboardVisible != mKeyboardVisible) { mKeyboardVisible = keyboardVisible; @@ -112,7 +106,7 @@ public class ClipboardOverlayWindow extends PhoneWindow void remove() { final View decorView = peekDecorView(); if (decorView != null && decorView.isAttachedToWindow()) { - mViewCaptureAwareWindowManager.removeViewImmediate(decorView); + mWindowManager.removeViewImmediate(decorView); } } @@ -146,7 +140,7 @@ public class ClipboardOverlayWindow extends PhoneWindow if (decorView.isAttachedToWindow()) { return; } - mViewCaptureAwareWindowManager.addView(decorView, mWindowLayoutParams); + mWindowManager.addView(decorView, mWindowLayoutParams); decorView.requestApplyInsets(); } @@ -167,7 +161,7 @@ public class ClipboardOverlayWindow extends PhoneWindow } final View decorView = peekDecorView(); if (decorView != null && decorView.isAttachedToWindow()) { - mViewCaptureAwareWindowManager.updateViewLayout(decorView, mWindowLayoutParams); + mWindowManager.updateViewLayout(decorView, mWindowLayoutParams); } } } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java index c86a84b17efe..7a60cce63a33 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java @@ -19,9 +19,7 @@ package com.android.systemui.clipboardoverlay.dagger; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static com.android.systemui.Flags.clipboardOverlayMultiuser; -import static com.android.systemui.Flags.enableViewCaptureTracing; import static com.android.systemui.shared.Flags.usePreferredImageEditor; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -31,8 +29,6 @@ import android.view.Display; import android.view.LayoutInflater; import android.view.WindowManager; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.clipboardoverlay.ActionIntentCreator; import com.android.systemui.clipboardoverlay.ClipboardOverlayView; import com.android.systemui.clipboardoverlay.DefaultIntentCreator; @@ -40,6 +36,7 @@ import com.android.systemui.clipboardoverlay.IntentCreator; import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import dagger.Lazy; import dagger.Module; @@ -89,21 +86,9 @@ public interface ClipboardOverlayModule { */ @Provides @OverlayWindowContext - static WindowManager provideWindowManager(@OverlayWindowContext Context context) { - return context.getSystemService(WindowManager.class); - } - - /** - * - */ - @Provides - @OverlayWindowContext - static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager( - @OverlayWindowContext WindowManager windowManager, - Lazy<ViewCapture> daggerLazyViewCapture) { - return new ViewCaptureAwareWindowManager(windowManager, - /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture), - /* isViewCaptureEnabled= */ enableViewCaptureTracing()); + static WindowManager provideWindowManager(@OverlayWindowContext Context context, + WindowManagerProvider windowManagerProvider) { + return windowManagerProvider.getWindowManager(context); } @Provides diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt index af8a5fa23ccb..8bfec0a5dac2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt @@ -21,8 +21,11 @@ import android.app.DreamManager import android.service.dreams.Flags.allowDreamWhenPostured import com.android.app.tracing.coroutines.launchInTraced import com.android.systemui.CoreStartable +import com.android.systemui.common.domain.interactor.BatteryInteractor +import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor import com.android.systemui.communal.posturing.shared.model.PosturedState +import com.android.systemui.communal.shared.model.WhenToDream import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.log.dagger.CommunalTableLog @@ -30,10 +33,14 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf +import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @SysUISingleton @@ -42,19 +49,36 @@ class DevicePosturingListener constructor( private val commandRegistry: CommandRegistry, private val dreamManager: DreamManager, - private val interactor: PosturingInteractor, + private val posturingInteractor: PosturingInteractor, + communalSettingsInteractor: CommunalSettingsInteractor, + batteryInteractor: BatteryInteractor, @Background private val bgScope: CoroutineScope, @CommunalTableLog private val tableLogBuffer: TableLogBuffer, ) : CoreStartable { private val command = DevicePosturingCommand() + // Only subscribe to posturing if applicable to avoid running the posturing CHRE nanoapp + // if posturing signal is not needed. + private val postured = + allOf( + batteryInteractor.isDevicePluggedIn, + communalSettingsInteractor.whenToDream.map { it == WhenToDream.WHILE_POSTURED }, + ) + .flatMapLatestConflated { shouldListen -> + if (shouldListen) { + posturingInteractor.postured + } else { + flowOf(false) + } + } + @SuppressLint("MissingPermission") override fun start() { if (!allowDreamWhenPostured()) { return } - interactor.postured + postured .distinctUntilChanged() .logDiffsForTable( tableLogBuffer = tableLogBuffer, @@ -78,7 +102,7 @@ constructor( val state = when (arg.lowercase()) { - "true" -> PosturedState.Postured(confidence = 1f) + "true" -> PosturedState.Postured "false" -> PosturedState.NotPostured "clear" -> PosturedState.Unknown else -> { @@ -87,7 +111,7 @@ constructor( null } } - state?.let { interactor.setValueForDebug(it) } + state?.let { posturingInteractor.setValueForDebug(it) } } override fun help(pw: PrintWriter) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt index 20bfabdc5fb9..678a5e2f6a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt @@ -57,7 +57,15 @@ constructor( allOf(batteryInteractor.isDevicePluggedIn, dockManager.retrieveIsDocked()) } WhenToStartHub.WHILE_CHARGING_AND_POSTURED -> { - allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured) + // Only listen to posturing if applicable to avoid running the posturing + // CHRE nanoapp when not needed. + batteryInteractor.isDevicePluggedIn.flatMapLatestConflated { isCharging -> + if (isCharging) { + posturingInteractor.postured + } else { + flowOf(false) + } + } } WhenToStartHub.NEVER -> flowOf(false) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt new file mode 100644 index 000000000000..21b8dd785f53 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt @@ -0,0 +1,48 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.posturing.data.model + +import androidx.annotation.FloatRange + +data class PositionState( + val stationary: StationaryState = StationaryState.Unknown, + val orientation: OrientationState = OrientationState.Unknown, +) { + sealed interface StationaryState { + @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float + + data object Unknown : StationaryState { + override val confidence: Float = 0f + } + + data class Stationary(override val confidence: Float) : StationaryState + + data class NotStationary(override val confidence: Float) : StationaryState + } + + sealed interface OrientationState { + @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float + + data object Unknown : OrientationState { + override val confidence: Float = 0f + } + + data class Postured(override val confidence: Float) : OrientationState + + data class NotPostured(override val confidence: Float) : OrientationState + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt index c5f357f556ca..d826685886d9 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt @@ -16,7 +16,7 @@ package com.android.systemui.communal.posturing.data.repository -import com.android.systemui.communal.posturing.shared.model.PosturedState +import com.android.systemui.communal.posturing.data.model.PositionState import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -25,6 +25,6 @@ import kotlinx.coroutines.flow.asStateFlow @SysUISingleton class NoOpPosturingRepository @Inject constructor() : PosturingRepository { - override val posturedState: Flow<PosturedState> = - MutableStateFlow(PosturedState.Unknown).asStateFlow() + override val positionState: Flow<PositionState> = + MutableStateFlow(PositionState()).asStateFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt index dae1a47f5be0..4de0a1e21d35 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt @@ -16,7 +16,7 @@ package com.android.systemui.communal.posturing.data.repository -import com.android.systemui.communal.posturing.shared.model.PosturedState +import com.android.systemui.communal.posturing.data.model.PositionState import kotlinx.coroutines.flow.Flow /** @@ -25,5 +25,5 @@ import kotlinx.coroutines.flow.Flow */ interface PosturingRepository { /** Whether the device is currently stationary and upright. */ - val posturedState: Flow<PosturedState> + val positionState: Flow<PositionState> } diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt index cd81dea9cad1..e487590d87d7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt @@ -16,26 +16,211 @@ package com.android.systemui.communal.posturing.domain.interactor +import android.annotation.SuppressLint +import android.hardware.Sensor +import android.hardware.TriggerEvent +import android.hardware.TriggerEventListener +import android.service.dreams.Flags.allowDreamWhenPostured +import com.android.systemui.communal.posturing.data.model.PositionState import com.android.systemui.communal.posturing.data.repository.PosturingRepository import com.android.systemui.communal.posturing.shared.model.PosturedState import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.log.dagger.CommunalLog +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf +import com.android.systemui.util.kotlin.slidingWindow +import com.android.systemui.util.sensors.AsyncSensorManager +import com.android.systemui.util.time.SystemClock +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn @SysUISingleton -class PosturingInteractor @Inject constructor(repository: PosturingRepository) { - private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown) +class PosturingInteractor +@Inject +constructor( + repository: PosturingRepository, + private val asyncSensorManager: AsyncSensorManager, + @Application private val applicationScope: CoroutineScope, + @Background private val bgDispatcher: CoroutineDispatcher, + @CommunalLog private val logBuffer: LogBuffer, + clock: SystemClock, +) { + private val logger = Logger(logBuffer, TAG) - val postured: Flow<Boolean> = - combine(repository.posturedState, debugPostured) { postured, debugValue -> - debugValue.asBoolean() ?: postured.asBoolean() ?: false - } + private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown) fun setValueForDebug(value: PosturedState) { debugPostured.value = value } + + /** + * Detects whether or not the device is stationary, applying a sliding window smoothing + * algorithm. + */ + private val stationarySmoothed: Flow<Boolean> = + merge( + observeTriggerSensor(Sensor.TYPE_PICK_UP_GESTURE) + // If pickup detected, avoid triggering posturing at all within the sliding + // window by emitting a negative infinity value. + .map { Float.NEGATIVE_INFINITY } + .onEach { logger.i("pickup gesture detected") }, + observeTriggerSensor(Sensor.TYPE_SIGNIFICANT_MOTION) + // If motion detected, avoid triggering posturing at all within the sliding + // window by emitting a negative infinity value. + .map { Float.NEGATIVE_INFINITY } + .onEach { logger.i("significant motion detected") }, + repository.positionState + .map { it.stationary } + .filterNot { it is PositionState.StationaryState.Unknown } + .map { stationaryState -> + if (stationaryState is PositionState.StationaryState.Stationary) { + stationaryState.confidence + } else { + // If not stationary, then we should effectively disable posturing by + // emitting the lowest possible confidence. + Float.NEGATIVE_INFINITY + } + }, + ) + .slidingWindow(SLIDING_WINDOW_DURATION, clock) + .filterNot { it.isEmpty() } + .map { window -> + val avgStationaryConfidence = window.average() + logger.i({ "stationary confidence: $double1 | window: $str1" }) { + str1 = window.formatWindowForDebugging() + double1 = avgStationaryConfidence + } + avgStationaryConfidence > CONFIDENCE_THRESHOLD + } + + /** + * Detects whether or not the device is in an upright orientation, applying a sliding window + * smoothing algorithm. + */ + private val orientationSmoothed: Flow<Boolean> = + repository.positionState + .map { it.orientation } + .filterNot { it is PositionState.OrientationState.Unknown } + .map { orientationState -> + if (orientationState is PositionState.OrientationState.Postured) { + orientationState.confidence + } else { + // If not postured, then we should effectively disable posturing by + // emitting the lowest possible confidence. + Float.NEGATIVE_INFINITY + } + } + .slidingWindow(SLIDING_WINDOW_DURATION, clock) + .filterNot { it.isEmpty() } + .map { window -> + val avgOrientationConfidence = window.average() + logger.i({ "orientation confidence: $double1 | window: $str1" }) { + str1 = window.formatWindowForDebugging() + double1 = avgOrientationConfidence + } + avgOrientationConfidence > CONFIDENCE_THRESHOLD + } + + /** + * Posturing is composed of the device being stationary and in the correct orientation. If both + * conditions are met, then consider it postured. + */ + private val posturedSmoothed: Flow<PosturedState> = + allOf(stationarySmoothed, orientationSmoothed) + .map { postured -> + if (postured) { + PosturedState.Postured + } else { + PosturedState.NotPostured + } + } + .flowOn(bgDispatcher) + .stateIn( + scope = applicationScope, + // Avoid losing the smoothing history if the user plug/unplugs rapidly. + started = + SharingStarted.WhileSubscribed( + stopTimeoutMillis = STOP_TIMEOUT_AFTER_UNSUBSCRIBE.inWholeMilliseconds, + replayExpirationMillis = 0, + ), + initialValue = PosturedState.Unknown, + ) + + /** + * Whether the device is postured. + * + * NOTE: Due to smoothing, this signal may be delayed to ensure we have a stable reading before + * being considered postured. + */ + val postured: Flow<Boolean> by lazy { + if (allowDreamWhenPostured()) { + combine(posturedSmoothed, debugPostured) { postured, debugValue -> + debugValue.asBoolean() ?: postured.asBoolean() ?: false + } + } else { + MutableStateFlow(false) + } + } + + /** + * Helper for observing a trigger sensor, which automatically unregisters itself after it + * executes once. + */ + private fun observeTriggerSensor(type: Int): Flow<Unit> = conflatedCallbackFlow { + val sensor = asyncSensorManager.getDefaultSensor(type) + val isRegistered = AtomicBoolean(false) + + fun registerCallbackInternal(callback: TriggerEventListener) { + if (isRegistered.compareAndSet(false, true)) { + asyncSensorManager.requestTriggerSensor(callback, sensor) + } + } + + val callback = + object : TriggerEventListener() { + override fun onTrigger(event: TriggerEvent) { + trySend(Unit) + if (isRegistered.getAndSet(false)) { + registerCallbackInternal(this) + } + } + } + + if (sensor != null) { + registerCallbackInternal(callback) + } + + awaitClose { + if (isRegistered.getAndSet(false)) { + asyncSensorManager.cancelTriggerSensor(callback, sensor) + } + } + } + + companion object { + const val TAG = "PosturingInteractor" + val SLIDING_WINDOW_DURATION = 10.seconds + const val CONFIDENCE_THRESHOLD = 0.8f + val STOP_TIMEOUT_AFTER_UNSUBSCRIBE = 5.seconds + } } fun PosturedState.asBoolean(): Boolean? { @@ -45,3 +230,8 @@ fun PosturedState.asBoolean(): Boolean? { PosturedState.Unknown -> null } } + +@SuppressLint("DefaultLocale") +fun List<Float>.formatWindowForDebugging(): String { + return joinToString(prefix = "[", postfix = "]") { String.format("%.2f", it) } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt index 431ca67315eb..c71cf14c4b52 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt @@ -18,7 +18,7 @@ package com.android.systemui.communal.posturing.shared.model sealed interface PosturedState { /** Represents postured state */ - data class Postured(val confidence: Float) : PosturedState + data object Postured : PosturedState /** Represents unknown/uninitialized state */ data object Unknown : PosturedState diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 7354f4096801..f45bafdfb17e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -16,9 +16,6 @@ package com.android.systemui.dagger; -import static com.android.systemui.Flags.enableViewCaptureTracing; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; - import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; @@ -103,7 +100,6 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.satellite.SatelliteManager; -import android.view.Choreographer; import android.view.CrossWindowBlurListeners; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -115,13 +111,9 @@ import android.view.accessibility.CaptioningManager; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassificationManager; -import androidx.annotation.NonNull; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.core.app.NotificationManagerCompat; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; -import com.android.app.viewcapture.ViewCaptureFactory; import com.android.internal.app.IBatteryStats; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.jank.InteractionJankMonitor; @@ -135,8 +127,9 @@ import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.user.utils.UserScopedService; import com.android.systemui.user.utils.UserScopedServiceImpl; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; +import com.android.systemui.utils.windowmanager.WindowManagerProviderImpl; -import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -713,38 +706,23 @@ public class FrameworkServicesModule { @Provides @Singleton - static WindowManager provideWindowManager(Context context) { - return context.getSystemService(WindowManager.class); - } - - /** A window manager working for the default display only. */ - @Provides - @Singleton - @Main - static WindowManager provideMainWindowManager(WindowManager windowManager) { - return windowManager; + static WindowManagerProvider provideWindowManagerProvider() { + return new WindowManagerProviderImpl(); } @Provides @Singleton - static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager( - WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) { - return new ViewCaptureAwareWindowManager(windowManager, - /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture), - /* isViewCaptureEnabled= */ enableViewCaptureTracing()); + static WindowManager provideWindowManager(Context context, + WindowManagerProvider windowManagerProvider) { + return windowManagerProvider.getWindowManager(context); } + /** A window manager working for the default display only. */ @Provides @Singleton - static ViewCaptureAwareWindowManager.Factory viewCaptureAwareWindowManagerFactory( - Lazy<ViewCapture> daggerLazyViewCapture) { - return new ViewCaptureAwareWindowManager.Factory() { - @NonNull - @Override - public ViewCaptureAwareWindowManager create(@NonNull WindowManager windowManager) { - return provideViewCaptureAwareWindowManager(windowManager, daggerLazyViewCapture); - } - }; + @Main + static WindowManager provideMainWindowManager(WindowManager windowManager) { + return windowManager; } @Provides @@ -835,12 +813,6 @@ public class FrameworkServicesModule { @Provides @Singleton - static ViewCapture provideViewCapture(Context context) { - return ViewCaptureFactory.getInstance(context); - } - - @Provides - @Singleton @Nullable static SupervisionManager provideSupervisionManager(Context context) { return (SupervisionManager) context.getSystemService(Context.SUPERVISION_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt index 7be323073692..9c3b9b273ab5 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt @@ -20,4 +20,8 @@ import android.content.Context import android.view.View /** Overlay to handle under-fingerprint sensor accessibility events. */ -class UdfpsAccessibilityOverlay(context: Context?) : View(context) +class UdfpsAccessibilityOverlay(context: Context?) : View(context) { + init { + accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE + } +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt index fa849bf5e413..1849bf20abdb 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt @@ -56,7 +56,7 @@ abstract class UdfpsAccessibilityOverlayViewModel( event.getPointerId(0), event, overlayParams, /* rotateToPortrait */ - false + false, ) if ( @@ -64,7 +64,7 @@ abstract class UdfpsAccessibilityOverlayViewModel( event.getPointerId(0), event, overlayParams, - /* rotateTouchToPortrait */ false + /* rotateTouchToPortrait */ false, ) ) { // view only receives motionEvents when [visible] which requires touchExplorationEnabled @@ -75,10 +75,10 @@ abstract class UdfpsAccessibilityOverlayViewModel( scaledTouch.x, scaledTouch.y, overlayParams, - /* touchRotatedToPortrait */ false + /* touchRotatedToPortrait */ false, ) if (announceStr != null) { - v.announceForAccessibility(announceStr) + v.contentDescription = announceStr } } // always let the motion events go through to underlying views diff --git a/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt b/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt new file mode 100644 index 000000000000..6fb3727db1f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt @@ -0,0 +1,65 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.dagger + +import android.content.Context +import android.view.Display +import com.android.app.displaylib.DisplayRepository +import com.android.systemui.coroutines.newTracingContext +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayAware +import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayId +import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.PerDisplaySingleton +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope + +/** Module providing common dependencies for per-display singletons. */ +@Module +class PerDisplayCommonModule { + + @Provides + @PerDisplaySingleton + fun provideDisplay(@DisplayId displayId: Int, displayRepository: DisplayRepository): Display { + return displayRepository.getDisplay(displayId) + ?: error("Couldn't get the display with id=$displayId") + } + + @Provides + @PerDisplaySingleton + @DisplayAware + fun provideDisplayContext( + display: Display, + @Application context: Context, + ): Context { + return context.createDisplayContext(display) + } + + @Provides + @PerDisplaySingleton + @DisplayAware + fun provideDisplayCoroutineScope( + @Background backgroundDispatcher: CoroutineDispatcher, + @DisplayId displayId: Int, + ): CoroutineScope { + return CoroutineScope( + backgroundDispatcher + newTracingContext("DisplayScope(id=$displayId)") + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt new file mode 100644 index 000000000000..f878aa1ea87b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt @@ -0,0 +1,55 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.dagger + +import dagger.BindsInstance +import dagger.Subcomponent +import javax.inject.Qualifier +import javax.inject.Scope +import kotlinx.coroutines.CoroutineScope + +/** + * Subcomponent for SysUI classes that should be instantiated once per display. + * + * All display specific classes should be provided with the @DisplayAware annotation. Once the + * display is removed, [displayCoroutineScope] gets cancelled. This means that if classes have some + * teardown step it should be executed when the scope is cancelled. Also note that the scope is + * cancelled in the background, so any teardown logic should be threadsafe. Cancelling on the main + * thread is not feasible as it would cause jank. + */ +@Subcomponent(modules = [PerDisplayCommonModule::class]) +interface SystemUIDisplaySubcomponent { + + @DisplayAware val displayCoroutineScope: CoroutineScope + + @Subcomponent.Factory + interface Factory { + fun create(@BindsInstance @DisplayId displayId: Int): SystemUIDisplaySubcomponent + } + + /** Scope annotation for singletons associated to a display. */ + @MustBeDocumented + @Retention(AnnotationRetention.RUNTIME) + @Scope + annotation class PerDisplaySingleton + + /** Qualifier used to represent that the object is provided/bound with the proper display. */ + @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayAware + + /** Annotates the display id inside the subcomponent. */ + @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayId +} diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt new file mode 100644 index 000000000000..a2d3b35740ea --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt @@ -0,0 +1,58 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.data.repository + +import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown +import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl +import com.android.app.displaylib.PerDisplayRepository +import com.android.app.tracing.traceSection +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import kotlinx.coroutines.cancel + +@SysUISingleton +class DisplayComponentInstanceProvider +@Inject +constructor(private val componentFactory: SystemUIDisplaySubcomponent.Factory) : + PerDisplayInstanceProviderWithTeardown<SystemUIDisplaySubcomponent> { + override fun createInstance(displayId: Int): SystemUIDisplaySubcomponent? = + runCatching { componentFactory.create(displayId) }.getOrNull() + + override fun destroyInstance(instance: SystemUIDisplaySubcomponent) { + traceSection("Destroying a display component instance") { + instance.displayCoroutineScope.cancel("Cancelling scope associated to the display.") + } + } +} + +@Module +object DisplayComponentRepository { + @SysUISingleton + @Provides + fun provideDisplayComponentRepository( + repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<SystemUIDisplaySubcomponent>, + instanceProvider: DisplayComponentInstanceProvider, + ): PerDisplayRepository<SystemUIDisplaySubcomponent> { + return repositoryFactory.create( + debugName = "DisplayComponentInstanceProvider", + instanceProvider, + ) + } +} 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 3390640fa6c6..aaaaacef001a 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 @@ -31,6 +31,7 @@ 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.android.systemui.utils.windowmanager.WindowManagerUtils import com.google.common.collect.HashBasedTable import com.google.common.collect.Table import java.io.PrintWriter @@ -110,7 +111,7 @@ constructor( return null } @SuppressLint("NonInjectedService") // Need to manually get the service - val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager + val windowManager = WindowManagerUtils.getWindowManager(context) val layoutInflater = LayoutInflater.from(context) DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater) } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index a2bcb98e36fe..fd716eea799a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -46,7 +46,6 @@ import androidx.lifecycle.LifecycleService; import androidx.lifecycle.ServiceLifecycleDispatcher; import androidx.lifecycle.ViewModelStore; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.compose.animation.scene.OverlayKey; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -117,7 +116,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ @Nullable private final ComponentName mHomeControlPanelDreamComponent; private final UiEventLogger mUiEventLogger; - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final String mWindowTitle; // A reference to the {@link Window} used to hold the dream overlay. @@ -378,7 +377,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Context context, DreamOverlayLifecycleOwner lifecycleOwner, @Main DelayableExecutor executor, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + WindowManager windowManager, ComplicationComponent.Factory complicationComponentFactory, DreamComplicationComponent.Factory dreamComplicationComponentFactory, DreamOverlayComponent.Factory dreamOverlayComponentFactory, @@ -403,7 +402,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ super(executor); mContext = context; mExecutor = executor; - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mScrimManager = scrimManager; mLowLightDreamComponent = lowLightDreamComponent; diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt index c2974a8d5429..c08a8e297174 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt @@ -20,7 +20,6 @@ import android.content.Context import android.graphics.Paint import android.graphics.PixelFormat import android.view.WindowManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyboard.docking.ui.KeyboardDockingIndicationView @@ -38,7 +37,7 @@ constructor( context: Context, @Application private val applicationScope: CoroutineScope, private val viewModel: KeyboardDockingIndicationViewModel, - private val windowManager: ViewCaptureAwareWindowManager, + private val windowManager: WindowManager ) { private val windowLayoutParams = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index a3796ab5ee27..cc03e49b462b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -145,7 +145,7 @@ constructor( KeyguardState.GLANCEABLE_HUB } else if (isOccluded && !isDreaming) { KeyguardState.OCCLUDED - } else if (hubV2 && isDreaming) { + } else if (isDreaming) { KeyguardState.DREAMING } else if (hubV2 && isIdleOnCommunal) { if (SceneContainerFlag.isEnabled) return@collect diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt index 1fd609dba637..853f1769994e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt @@ -26,7 +26,6 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.WindowManager import android.widget.ProgressBar import androidx.core.view.isGone -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R import javax.inject.Inject @@ -38,7 +37,7 @@ class SideFpsProgressBar @Inject constructor( private val layoutInflater: LayoutInflater, - private val windowManager: ViewCaptureAwareWindowManager, + private val windowManager: WindowManager, ) { private var overlayView: View? = null @@ -91,7 +90,7 @@ constructor( ) { if (overlayView == null) { overlayView = layoutInflater.inflate(R.layout.sidefps_progress_bar, null, false) - windowManager.addView(requireNotNull(overlayView), overlayViewParams) + windowManager.addView(overlayView, overlayViewParams) progressBar?.pivotX = 0.0f progressBar?.pivotY = 0.0f } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt index 2cd5016cb206..d053dd2b54d1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt @@ -248,7 +248,7 @@ constructor( // Update loading state with actual active value mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let { mediaFilterRepository.addMediaDataLoadingState( - MediaDataLoadingModel.Loaded(lastActiveId, immediately) + MediaDataLoadingModel.Loaded(lastActiveId) ) mediaLogger.logMediaLoaded(lastActiveId, it.active, "expiring reactivated id") listeners.forEach { listener -> diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt index c8a02faea58a..323e5cb76aba 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt @@ -26,13 +26,10 @@ sealed class MediaDataLoadingModel { /** Media data has been loaded. */ data class Loaded( override val instanceId: InstanceId, - val immediatelyUpdateUi: Boolean = true, val receivedSmartspaceCardLatency: Int = 0, val isSsReactivated: Boolean = false, ) : MediaDataLoadingModel() /** Media data has been removed. */ - data class Removed( - override val instanceId: InstanceId, - ) : MediaDataLoadingModel() + data class Removed(override val instanceId: InstanceId) : MediaDataLoadingModel() } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 71b3223b77be..800220ee962a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -68,7 +68,7 @@ import com.android.systemui.media.controls.ui.view.MediaHostState import com.android.systemui.media.controls.ui.view.MediaScrollView import com.android.systemui.media.controls.ui.view.MediaViewHolder import com.android.systemui.media.controls.ui.viewmodel.MediaCarouselViewModel -import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel +import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager @@ -324,8 +324,8 @@ constructor( private var widthInSceneContainerPx = 0 private var heightInSceneContainerPx = 0 - private val controllerById = mutableMapOf<String, MediaViewController>() - private val commonViewModels = mutableListOf<MediaCommonViewModel>() + private val controllerById = mutableMapOf<InstanceId, MediaViewController>() + private val controlViewModels = mutableListOf<MediaControlViewModel>() private val isOnGone = keyguardTransitionInteractor @@ -566,10 +566,10 @@ constructor( private fun listenForMediaItemsChanges(scope: CoroutineScope): Job { return scope.launch { mediaCarouselViewModel.mediaItems.collectLatest { - val diffUtilCallback = MediaViewModelCallback(commonViewModels, it) + val diffUtilCallback = MediaViewModelCallback(controlViewModels, it) val listUpdateCallback = MediaViewModelListUpdateCallback( - old = commonViewModels, + old = controlViewModels, new = it, onAdded = this@MediaCarouselController::onAdded, onUpdated = this@MediaCarouselController::onUpdated, @@ -590,7 +590,7 @@ constructor( } private fun onAdded( - commonViewModel: MediaCommonViewModel, + controlViewModel: MediaControlViewModel, position: Int, configChanged: Boolean = false, ) { @@ -601,64 +601,52 @@ constructor( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, ) - when (commonViewModel) { - is MediaCommonViewModel.MediaControl -> { - val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent) - viewController.widthInSceneContainerPx = widthInSceneContainerPx - viewController.heightInSceneContainerPx = heightInSceneContainerPx - viewController.attachPlayer(viewHolder) - viewController.mediaViewHolder?.player?.layoutParams = lp - if (configChanged) { - commonViewModel.controlViewModel.onMediaConfigChanged() - } - MediaControlViewBinder.bind( - viewHolder, - commonViewModel.controlViewModel, - viewController, - falsingManager, - backgroundDispatcher, - mainDispatcher, - ) - mediaContent.addView(viewHolder.player, position) - controllerById[commonViewModel.instanceId.toString()] = viewController - } + val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent) + viewController.widthInSceneContainerPx = widthInSceneContainerPx + viewController.heightInSceneContainerPx = heightInSceneContainerPx + viewController.attachPlayer(viewHolder) + viewController.mediaViewHolder?.player?.layoutParams = lp + if (configChanged) { + controlViewModel.onMediaConfigChanged() } + MediaControlViewBinder.bind( + viewHolder, + controlViewModel, + viewController, + falsingManager, + backgroundDispatcher, + mainDispatcher, + ) + mediaContent.addView(viewHolder.player, position) + controllerById[controlViewModel.instanceId] = viewController viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded) updateViewControllerToState(viewController, noAnimation = true) updatePageIndicator() - if ( - commonViewModel is MediaCommonViewModel.MediaControl && commonViewModel.isMediaFromRec - ) { - mediaCarouselScrollHandler.scrollToPlayer( - mediaCarouselScrollHandler.visibleMediaIndex, - destIndex = 0, - ) - } mediaCarouselScrollHandler.onPlayersChanged() mediaFrame.requiresRemeasuring = true - commonViewModel.onAdded(commonViewModel) + controlViewModel.onAdded(controlViewModel) } - private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) { - commonViewModel.onUpdated(commonViewModel) + private fun onUpdated(controlViewModel: MediaControlViewModel, position: Int) { + controlViewModel.onUpdated(controlViewModel) updatePageIndicator() mediaCarouselScrollHandler.onPlayersChanged() } - private fun onRemoved(commonViewModel: MediaCommonViewModel) { - val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString() + private fun onRemoved(controlViewModel: MediaControlViewModel) { + val id = controlViewModel.instanceId controllerById.remove(id)?.let { mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player) mediaContent.removeView(it.mediaViewHolder!!.player) it.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() - commonViewModel.onRemoved(true) + controlViewModel.onRemoved(true) } } - private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) { - val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString() + private fun onMoved(controlViewModel: MediaControlViewModel, from: Int, to: Int) { + val id = controlViewModel.instanceId controllerById[id]?.let { mediaContent.removeViewAt(from) mediaContent.addView(it.mediaViewHolder!!.player, to) @@ -667,19 +655,12 @@ constructor( mediaCarouselScrollHandler.onPlayersChanged() } - private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) { - commonViewModels.clear() - commonViewModels.addAll(viewModels) + private fun setNewViewModelsList(viewModels: List<MediaControlViewModel>) { + controlViewModels.clear() + controlViewModels.addAll(viewModels) // Ensure we only show the needed UMOs in media carousel. - val viewIds = - viewModels - .map { mediaCommonViewModel -> - (mediaCommonViewModel as MediaCommonViewModel.MediaControl) - .instanceId - .toString() - } - .toHashSet() + val viewIds = viewModels.map { controlViewModel -> controlViewModel.instanceId }.toHashSet() controllerById .filter { !viewIds.contains(it.key) } .forEach { @@ -976,9 +957,8 @@ constructor( ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator)) if (recreateMedia) { mediaContent.removeAllViews() - commonViewModels.forEachIndexed { index, viewModel -> - val mediaControlViewModel = (viewModel as MediaCommonViewModel.MediaControl) - controllerById[mediaControlViewModel.instanceId.toString()]?.onDestroy() + controlViewModels.forEachIndexed { index, viewModel -> + controllerById[viewModel.instanceId]?.onDestroy() onAdded(viewModel, index, configChanged = true) } } @@ -1309,7 +1289,7 @@ constructor( println("dataKeys: ${MediaPlayerData.dataKeys()}") println("orderedPlayerSortKeys: ${MediaPlayerData.playerKeys()}") println("visiblePlayerSortKeys: ${MediaPlayerData.visiblePlayerKeys()}") - println("commonViewModels: $commonViewModels") + println("controlViewModels: $controlViewModels") println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}") println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}") println("current size: $currentCarouselWidth x $currentCarouselHeight") diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt index 2fc44ad3cce6..ad2d46263faf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt @@ -17,12 +17,12 @@ package com.android.systemui.media.controls.ui.util import androidx.recyclerview.widget.DiffUtil -import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel +import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel /** A [DiffUtil.Callback] to calculate difference between old and new media view-model list. */ class MediaViewModelCallback( - private val old: List<MediaCommonViewModel>, - private val new: List<MediaCommonViewModel>, + private val old: List<MediaControlViewModel>, + private val new: List<MediaControlViewModel>, ) : DiffUtil.Callback() { override fun getOldListSize(): Int { @@ -36,27 +36,12 @@ class MediaViewModelCallback( override fun areItemsTheSame(oldIndex: Int, newIndex: Int): Boolean { val oldItem = old[oldIndex] val newItem = new[newIndex] - return if ( - oldItem is MediaCommonViewModel.MediaControl && - newItem is MediaCommonViewModel.MediaControl - ) { - oldItem.instanceId == newItem.instanceId - } else { - false - } + return oldItem.instanceId == newItem.instanceId } override fun areContentsTheSame(oldIndex: Int, newIndex: Int): Boolean { val oldItem = old[oldIndex] val newItem = new[newIndex] - return if ( - oldItem is MediaCommonViewModel.MediaControl && - newItem is MediaCommonViewModel.MediaControl - ) { - oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi && - oldItem.updateTime == newItem.updateTime - } else { - false - } + return oldItem.updateTime == newItem.updateTime } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt index 6022b7b1fc13..1db8c58d4140 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt @@ -17,17 +17,17 @@ package com.android.systemui.media.controls.ui.util import androidx.recyclerview.widget.ListUpdateCallback -import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel +import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel import kotlin.math.min /** A [ListUpdateCallback] to apply media events needed to reach the new state. */ class MediaViewModelListUpdateCallback( - private val old: List<MediaCommonViewModel>, - private val new: List<MediaCommonViewModel>, - private val onAdded: (MediaCommonViewModel, Int) -> Unit, - private val onUpdated: (MediaCommonViewModel, Int) -> Unit, - private val onRemoved: (MediaCommonViewModel) -> Unit, - private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit, + private val old: List<MediaControlViewModel>, + private val new: List<MediaControlViewModel>, + private val onAdded: (MediaControlViewModel, Int) -> Unit, + private val onUpdated: (MediaControlViewModel, Int) -> Unit, + private val onRemoved: (MediaControlViewModel) -> Unit, + private val onMoved: (MediaControlViewModel, Int, Int) -> Unit, ) : ListUpdateCallback { override fun onInserted(position: Int, count: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index dfaee4434bcf..54d3151694bd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -56,7 +56,7 @@ constructor( val hasAnyMediaOrRecommendations: StateFlow<Boolean> = interactor.hasAnyMediaOrRecommendation val hasActiveMediaOrRecommendations: StateFlow<Boolean> = interactor.hasActiveMediaOrRecommendation - val mediaItems: StateFlow<List<MediaCommonViewModel>> = + val mediaItems: StateFlow<List<MediaControlViewModel>> = interactor.currentMedia .map { sortedItems -> val mediaList = buildList { @@ -91,8 +91,7 @@ constructor( var updateHostVisibility: () -> Unit = {} - private val mediaControlByInstanceId = - mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>() + private val mediaControlByInstanceId = mutableMapOf<InstanceId, MediaControlViewModel>() private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf() @@ -108,18 +107,16 @@ constructor( interactor.reorderMedia() } - private fun toViewModel( - commonModel: MediaCommonModel.MediaControl - ): MediaCommonViewModel.MediaControl { + private fun toViewModel(commonModel: MediaCommonModel.MediaControl): MediaControlViewModel { val instanceId = commonModel.mediaLoadedModel.instanceId - return mediaControlByInstanceId[instanceId]?.copy( - immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi, - updateTime = commonModel.updateTime, - ) - ?: MediaCommonViewModel.MediaControl( + return mediaControlByInstanceId[instanceId]?.copy(updateTime = commonModel.updateTime) + ?: MediaControlViewModel( + applicationContext = applicationContext, + backgroundDispatcher = backgroundDispatcher, + backgroundExecutor = backgroundExecutor, + interactor = controlInteractorFactory.create(instanceId), + logger = logger, instanceId = instanceId, - immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi, - controlViewModel = createMediaControlViewModel(instanceId), onAdded = { mediaLogger.logMediaCardAdded(instanceId) onMediaControlAddedOrUpdated(it, commonModel) @@ -130,31 +127,20 @@ constructor( mediaLogger.logMediaCardRemoved(instanceId) }, onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) }, - isMediaFromRec = commonModel.isMediaFromRec, updateTime = commonModel.updateTime, ) .also { mediaControlByInstanceId[instanceId] = it } } - private fun createMediaControlViewModel(instanceId: InstanceId): MediaControlViewModel { - return MediaControlViewModel( - applicationContext = applicationContext, - backgroundDispatcher = backgroundDispatcher, - backgroundExecutor = backgroundExecutor, - interactor = controlInteractorFactory.create(instanceId), - logger = logger, - ) - } - private fun onMediaControlAddedOrUpdated( - commonViewModel: MediaCommonViewModel, + controlViewModel: MediaControlViewModel, commonModel: MediaCommonModel.MediaControl, ) { if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) { // This media control is due for removal as it is now paused + timed out, and resumption // setting is off. if (isReorderingAllowed()) { - commonViewModel.onRemoved(true) + controlViewModel.onRemoved(true) } else { modelsPendingRemoval.add(commonModel) } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt deleted file mode 100644 index d493d57051f7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt +++ /dev/null @@ -1,38 +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.media.controls.ui.viewmodel - -import com.android.internal.logging.InstanceId - -/** Models media view model UI state. */ -sealed class MediaCommonViewModel { - - abstract val onAdded: (MediaCommonViewModel) -> Unit - abstract val onRemoved: (Boolean) -> Unit - abstract val onUpdated: (MediaCommonViewModel) -> Unit - - data class MediaControl( - val instanceId: InstanceId, - val immediatelyUpdateUi: Boolean, - val controlViewModel: MediaControlViewModel, - override val onAdded: (MediaCommonViewModel) -> Unit, - override val onRemoved: (Boolean) -> Unit, - override val onUpdated: (MediaCommonViewModel) -> Unit, - val isMediaFromRec: Boolean = false, - val updateTime: Long = 0, - ) : MediaCommonViewModel() -} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt index 015274a10330..31cfb8479dd1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt @@ -45,12 +45,17 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** Models UI state and handles user input for a media control. */ -class MediaControlViewModel( +data class MediaControlViewModel( @Application private val applicationContext: Context, @Background private val backgroundDispatcher: CoroutineDispatcher, @Background private val backgroundExecutor: Executor, private val interactor: MediaControlInteractor, private val logger: MediaUiEventLogger, + val instanceId: InstanceId, + val onAdded: (MediaControlViewModel) -> Unit, + val onRemoved: (Boolean) -> Unit, + val onUpdated: (MediaControlViewModel) -> Unit, + val updateTime: Long = 0, ) { val player: Flow<MediaPlayerViewModel?> = interactor.mediaControl diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java index ac1672db9375..e3990d25f94e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java @@ -237,8 +237,7 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl clickListener = v -> onItemClick(v, device); } } else { - deviceStatusIcon = getDeviceStatusIcon(device, - device.hasOngoingSession()); + deviceStatusIcon = getDeviceStatusIcon(device); clickListener = getClickListenerBasedOnSelectionBehavior(device); } deviceDisabled = clickListener == null; @@ -302,12 +301,8 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl } @Nullable - private Drawable getDeviceStatusIcon(MediaDevice device, boolean hasOngoingSession) { - if (hasOngoingSession) { - return mContext.getDrawable(R.drawable.ic_sound_bars_anim); - } else { - return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext); - } + private Drawable getDeviceStatusIcon(MediaDevice device) { + return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext); } protected void onExpandGroupButtonClicked() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java index 6ab4a52dc919..7ab6b3cfb8b1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java @@ -231,7 +231,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { updateFullItemClickListener(clickListener); updateContentAlpha(deviceDisabled); updateSubtitle(subtitle); - updateDeviceStatusIcon(deviceStatusIcon); + updateDeviceStatusIcon(deviceStatusIcon, ongoingSessionStatus, connectionState); updateItemBackground(connectionState); } @@ -523,11 +523,20 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase { mStatusIcon.setAlpha(alphaValue); } - private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon) { - if (deviceStatusIcon == null) { + private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon, + @Nullable OngoingSessionStatus ongoingSessionStatus, + ConnectionState connectionState) { + boolean showOngoingSession = + ongoingSessionStatus != null && connectionState == ConnectionState.DISCONNECTED; + if (deviceStatusIcon == null && !showOngoingSession) { mStatusIcon.setVisibility(View.GONE); } else { - mStatusIcon.setImageDrawable(deviceStatusIcon); + if (showOngoingSession) { + mStatusIcon.setImageDrawable( + mContext.getDrawable(R.drawable.ic_sound_bars_anim)); + } else { + mStatusIcon.setImageDrawable(deviceStatusIcon); + } mStatusIcon.setImageTintList(ColorStateList.valueOf( mController.getColorSchemeLegacy().getColorItemContent())); if (deviceStatusIcon instanceof AnimatedVectorDrawable) { diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 2a23620839e5..6a2c4519e1a5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -35,7 +35,6 @@ import android.view.ViewGroup import android.view.WindowManager import android.view.accessibility.AccessibilityManager import com.android.app.animation.Interpolators -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.InstanceId import com.android.internal.widget.CachingIconView import com.android.systemui.common.shared.model.ContentDescription @@ -73,7 +72,7 @@ constructor( private val commandQueue: CommandQueue, context: Context, logger: MediaTttReceiverLogger, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, @Main private val mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, @@ -90,7 +89,7 @@ constructor( TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>( context, logger, - viewCaptureAwareWindowManager, + windowManager, mainExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt index 9265bfb2f66b..5ffa7fa0ef8d 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt @@ -26,13 +26,13 @@ import android.graphics.Shader import android.util.AttributeSet import android.view.View import android.view.WindowManager -import androidx.core.content.getSystemService import androidx.core.content.res.use import com.android.systemui.res.R import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.shared.recents.model.ThumbnailData import com.android.systemui.shared.recents.utilities.PreviewPositionHelper import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen +import com.android.systemui.utils.windowmanager.WindowManagerUtils /** * Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData]. @@ -53,7 +53,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 } } - private val windowManager: WindowManager = context.getSystemService()!! + private val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context) private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = defaultBackgroundColor } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt index c829471f53f3..57fd1e790f0b 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt @@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector.view import android.content.Context import android.content.res.Configuration import android.graphics.Rect +import android.view.WindowManager import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope @@ -36,6 +37,7 @@ constructor( private val context: Context, private val windowMetricsProvider: WindowMetricsProvider, private val configurationController: ConfigurationController, + private val windowManager: WindowManager, ) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver { /** Returns the size of the task preview on the screen in pixels */ @@ -65,7 +67,7 @@ constructor( val width = maxWindowBounds.width() var height = maximumWindowHeight - val isLargeScreen = isLargeScreen(context) + val isLargeScreen = isLargeScreen(windowManager, context.resources) if (isLargeScreen) { val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom height -= taskbarSize diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java index 39482bea0111..c5c8c01f8b39 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java @@ -16,17 +16,12 @@ package com.android.systemui.navigationbar; -import static com.android.systemui.Flags.enableViewCaptureTracing; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; - import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import com.android.app.displaylib.PerDisplayRepository; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; @@ -34,8 +29,8 @@ import com.android.systemui.navigationbar.views.NavigationBarFrame; import com.android.systemui.navigationbar.views.NavigationBarView; import com.android.systemui.res.R; import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; -import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -70,8 +65,9 @@ public interface NavigationBarModule { @Provides @NavigationBarScope @DisplayId - static WindowManager provideWindowManager(@DisplayId Context context) { - return context.getSystemService(WindowManager.class); + static WindowManager provideWindowManager(@DisplayId Context context, + WindowManagerProvider windowManagerProvider) { + return windowManagerProvider.getWindowManager(context); } /** A SysUiState for the navigation bar display. */ @@ -87,15 +83,4 @@ public interface NavigationBarModule { return defaultState; } } - - /** A ViewCaptureAwareWindowManager specific to the display's context. */ - @Provides - @NavigationBarScope - @DisplayId - static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager( - @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) { - return new ViewCaptureAwareWindowManager(windowManager, - /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture), - /* isViewCaptureEnabled= */ enableViewCaptureTracing()); - } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 44c828731e24..6d7492686985 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -33,7 +33,6 @@ import androidx.annotation.VisibleForTesting import androidx.core.os.postDelayed import androidx.core.view.isVisible import androidx.dynamicanimation.animation.DynamicAnimation -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.jank.Cuj import com.android.internal.jank.InteractionJankMonitor import com.android.internal.util.LatencyTracker @@ -85,7 +84,7 @@ class BackPanelController @AssistedInject constructor( @Assisted context: Context, - private val windowManager: ViewCaptureAwareWindowManager, + private val windowManager: WindowManager, private val viewConfiguration: ViewConfiguration, @Assisted private val mainHandler: Handler, private val systemClock: SystemClock, diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java index 8b5b3adeef1f..ad0acbdaf702 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java @@ -104,7 +104,6 @@ import android.view.inputmethod.InputMethodManager; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -199,7 +198,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private final Context mContext; private final Bundle mSavedState; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final AccessibilityManager mAccessibilityManager; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; @@ -560,7 +558,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements @Nullable Bundle savedState, @DisplayId Context context, @DisplayId WindowManager windowManager, - @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, DeviceProvisionedController deviceProvisionedController, @@ -605,7 +602,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mContext = context; mSavedState = savedState; mWindowManager = windowManager; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; mAccessibilityManager = accessibilityManager; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; @@ -726,7 +722,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView); try { - mViewCaptureAwareWindowManager.addView( + mWindowManager.addView( mFrame, getBarLayoutParams( mContext.getResources() @@ -783,7 +779,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mCommandQueue.removeCallback(this); Trace.beginSection("NavigationBar#removeViewImmediate"); try { - mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView()); + mWindowManager.removeViewImmediate(mView.getRootView()); } catch (IllegalArgumentException e) { // Wrapping this in a try/catch to avoid crashes when a display is instantly removed // after being added, and initialization hasn't finished yet. @@ -888,7 +884,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements resetSecondaryHandle(); getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); try { - mViewCaptureAwareWindowManager.removeView(mOrientationHandle); + mWindowManager.removeView(mOrientationHandle); } catch (IllegalArgumentException e) { // Wrapping this in a try/catch to avoid crashes when a display is instantly removed // after being added, and initialization hasn't finished yet. @@ -967,7 +963,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; try { - mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams); + mWindowManager.addView(mOrientationHandle, mOrientationParams); } catch (WindowManager.InvalidDisplayException e) { // Wrapping this in a try/catch to avoid crashes when a display is instantly removed // after being added, and initialization hasn't finished yet. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java index cbc4c26b2f94..d2974e9f90bd 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java @@ -87,6 +87,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarTransitionsController; +import com.android.systemui.utils.windowmanager.WindowManagerUtils; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.pip.Pip; @@ -729,7 +730,7 @@ public class NavigationBarView extends FrameLayout { } else { return; } - WindowManager wm = getContext().getSystemService(WindowManager.class); + WindowManager wm = WindowManagerUtils.getWindowManager(getContext()); wm.updateViewLayout((View) getParent(), lp); } } diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt index 9319961f5b68..0a5222e3df44 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt @@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.UserActionResult.HideOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays +import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel @@ -38,11 +37,8 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() : mapOf( Swipe.Up to HideOverlay(Overlays.NotificationsShade), Back to HideOverlay(Overlays.NotificationsShade), - Swipe.Down(fromSource = SceneContainerArea.EndHalf) to - ShowOverlay( - Overlays.QuickSettingsShade, - hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade), - ), + Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to + ReplaceByOverlay(Overlays.QuickSettingsShade), ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java index 2ecca2d8c776..f37a86e2b9d5 100644 --- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java +++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java @@ -16,8 +16,6 @@ package com.android.systemui.power; -import static com.android.systemui.Flags.enableViewCaptureTracing; - import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; @@ -30,28 +28,21 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; - -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.res.R; -import kotlin.Lazy; - /** * View that shows a warning shortly before the device goes into sleep * after prolonged user inactivity when bound to. */ public class InattentiveSleepWarningView extends FrameLayout { private final IBinder mWindowToken = new Binder(); - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private Animator mFadeOutAnimator; private boolean mDismissing; - InattentiveSleepWarningView(Context context, Lazy<ViewCapture> lazyViewCapture) { + InattentiveSleepWarningView(Context context, WindowManager windowManager) { super(context); - WindowManager wm = mContext.getSystemService(WindowManager.class); - mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture, - enableViewCaptureTracing()); + mWindowManager = windowManager; final LayoutInflater layoutInflater = LayoutInflater.from(mContext); layoutInflater.inflate(R.layout.inattentive_sleep_warning, this, true /* attachToRoot */); diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 861a7ce282af..4e1bfd15e58c 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -16,8 +16,6 @@ package com.android.systemui.power; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; - import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -42,11 +40,11 @@ import android.service.vr.IVrStateCallbacks; import android.text.format.DateUtils; import android.util.Log; import android.util.Slog; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.app.viewcapture.ViewCapture; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.utils.ThreadUtils; @@ -59,8 +57,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; -import kotlin.Lazy; - import java.io.PrintWriter; import java.util.Arrays; import java.util.concurrent.Future; @@ -120,9 +116,9 @@ public class PowerUI implements private IThermalEventListener mSkinThermalEventListener; private IThermalEventListener mUsbThermalEventListener; private final Context mContext; + private final WindowManager mWindowManager; private final BroadcastDispatcher mBroadcastDispatcher; private final CommandQueue mCommandQueue; - private final Lazy<ViewCapture> mLazyViewCapture; @Nullable private final IVrManager mVrManager; private final WakefulnessLifecycle.Observer mWakefulnessObserver = @@ -164,7 +160,7 @@ public class PowerUI implements WakefulnessLifecycle wakefulnessLifecycle, PowerManager powerManager, UserTracker userTracker, - dagger.Lazy<ViewCapture> daggerLazyViewCapture) { + WindowManager windowManager) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mCommandQueue = commandQueue; @@ -174,7 +170,7 @@ public class PowerUI implements mPowerManager = powerManager; mWakefulnessLifecycle = wakefulnessLifecycle; mUserTracker = userTracker; - mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture); + mWindowManager = windowManager; } public void start() { @@ -649,7 +645,7 @@ public class PowerUI implements @Override public void showInattentiveSleepWarning() { if (mOverlayView == null) { - mOverlayView = new InattentiveSleepWarningView(mContext, mLazyViewCapture); + mOverlayView = new InattentiveSleepWarningView(mContext, mWindowManager); } mOverlayView.show(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt index 43e3de2040d7..0236256ec9ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt @@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.asQSTileIcon +import com.android.systemui.qs.flags.QsInCompose import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider @@ -88,7 +89,11 @@ constructor( init { lifecycle.coroutineScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + lifecycle.repeatOnLifecycle( + // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset + // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined. + if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED + ) { dataInteractor.tileData().collect { refreshState(it) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt index fcfa46f07ab4..6a842c31a3aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt @@ -31,7 +31,6 @@ import com.android.systemui.animation.Expandable import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.modes.shared.ModesUi -import com.android.systemui.modes.shared.ModesUiIcons import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile @@ -40,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.asQSTileIcon +import com.android.systemui.qs.flags.QsInCompose import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider @@ -88,10 +88,12 @@ constructor( private val config = qsTileConfigProvider.getConfig(TILE_SPEC) init { - /* Check if */ ModesUiIcons.isUnexpectedlyInLegacyMode() - lifecycle.coroutineScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + lifecycle.repeatOnLifecycle( + // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset + // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined. + if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED + ) { dataInteractor.tileData().collect { refreshState(it) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java index 6b5a22a4fc09..945e051606b9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java @@ -65,7 +65,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -186,7 +185,7 @@ public class InternetDetailsContentController implements AccessPointController.A private GlobalSettings mGlobalSettings; private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback; - private ViewCaptureAwareWindowManager mWindowManager; + private WindowManager mWindowManager; private ToastFactory mToastFactory; private SignalDrawable mSignalDrawable; private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS @@ -252,7 +251,7 @@ public class InternetDetailsContentController implements AccessPointController.A @Main Handler handler, @Main Executor mainExecutor, BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings, KeyguardStateController keyguardStateController, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, ToastFactory toastFactory, + @ShadeDisplayAware WindowManager windowManager, ToastFactory toastFactory, @Background Handler workerHandler, CarrierConfigTracker carrierConfigTracker, LocationController locationController, @@ -284,7 +283,7 @@ public class InternetDetailsContentController implements AccessPointController.A mAccessPointController = accessPointController; mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext); mConnectivityManagerNetworkCallback = new DataConnectivityListener(); - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mToastFactory = toastFactory; mSignalDrawable = new SignalDrawable(mContext); mSecondarySignalDrawable = new SignalDrawable(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt index f9b1a36621b2..11622be3d417 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt @@ -77,7 +77,7 @@ constructor( val showMedia: Boolean by hydrator.hydratedStateOf( traceName = "showMedia", - source = mediaCarouselInteractor.hasActiveMediaOrRecommendation, + source = mediaCarouselInteractor.hasAnyMediaOrRecommendation, ) override suspend fun onActivated(): Nothing { diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt index 52c4e2fac6d5..2f7bc0992411 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt @@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.UserActionResult.HideOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay -import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays +import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.viewmodel.SceneContainerArea @@ -47,12 +46,8 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM put(Back, HideOverlay(Overlays.QuickSettingsShade)) } put( - Swipe.Down(fromSource = SceneContainerArea.StartHalf), - ShowOverlay( - Overlays.NotificationsShade, - hideCurrentOverlays = - HideCurrentOverlays.Some(Overlays.QuickSettingsShade), - ), + Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf), + ReplaceByOverlay(Overlays.NotificationsShade), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 432a35a1a3dd..a1281ec23f92 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,9 +16,7 @@ package com.android.systemui.recents; -import static com.android.systemui.Flags.enableViewCaptureTracing; import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -55,8 +53,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.CoreStartable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; @@ -87,7 +83,6 @@ public class ScreenPinningRequest implements private final Lazy<NavigationBarController> mNavigationBarControllerLazy; private final AccessibilityManager mAccessibilityService; private final WindowManager mWindowManager; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final BroadcastDispatcher mBroadcastDispatcher; private final UserTracker mUserTracker; @@ -112,15 +107,12 @@ public class ScreenPinningRequest implements Lazy<NavigationBarController> navigationBarControllerLazy, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, - Lazy<ViewCapture> daggerLazyViewCapture) { + WindowManager windowManager) { mContext = context; mNavigationBarControllerLazy = navigationBarControllerLazy; mAccessibilityService = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); - mWindowManager = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); - mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager, - toKotlinLazy(daggerLazyViewCapture), enableViewCaptureTracing()); + mWindowManager = windowManager; mNavBarMode = navigationModeController.addListener(this); mBroadcastDispatcher = broadcastDispatcher; mUserTracker = userTracker; @@ -131,7 +123,7 @@ public class ScreenPinningRequest implements public void clearPrompt() { if (mRequestWindow != null) { - mViewCaptureAwareWindowManager.removeView(mRequestWindow); + mWindowManager.removeView(mRequestWindow); mRequestWindow = null; } } @@ -152,7 +144,7 @@ public class ScreenPinningRequest implements // show the confirmation WindowManager.LayoutParams lp = getWindowLayoutParams(); - mViewCaptureAwareWindowManager.addView(mRequestWindow, lp); + mWindowManager.addView(mRequestWindow, lp); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt index ede453dbe6b3..760b97e6245b 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt @@ -58,6 +58,22 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re } ) + data object TopEdgeStartHalf : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.TopEdgeLeftHalf + else Resolved.TopEdgeRightHalf + } + ) + + data object TopEdgeEndHalf : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.TopEdgeRightHalf + else Resolved.TopEdgeLeftHalf + } + ) + override fun resolve(layoutDirection: LayoutDirection): Resolved { return resolveArea(layoutDirection) } @@ -72,6 +88,12 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re data object RightEdge : Resolved data object RightHalf : Resolved + + /** The left half of the top edge of the display. */ + data object TopEdgeLeftHalf : Resolved + + /** The right half of the top edge of the display. */ + data object TopEdgeRightHalf : Resolved } } @@ -108,9 +130,14 @@ class SceneContainerSwipeDetector(val edgeSize: Dp) : SwipeSourceDetector { Edge.Resolved.Left -> SceneContainerArea.Resolved.LeftEdge Edge.Resolved.Bottom -> SceneContainerArea.Resolved.BottomEdge Edge.Resolved.Right -> SceneContainerArea.Resolved.RightEdge - else -> { - // Note: This intentionally includes Edge.Resolved.Top. At the moment, we don't need - // to detect swipes on the top edge, and consider them part of the right/left half. + Edge.Resolved.Top -> { + if (position.x < layoutSize.width * 0.5f) { + SceneContainerArea.Resolved.TopEdgeLeftHalf + } else { + SceneContainerArea.Resolved.TopEdgeRightHalf + } + } + null -> { if (position.x < layoutSize.width * 0.5f) { SceneContainerArea.Resolved.LeftHalf } else { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt index c4fe7a428084..644e12cba6fc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt @@ -31,7 +31,6 @@ import android.view.Window import android.view.WindowInsets import android.view.WindowManager import android.window.WindowContext -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.policy.PhoneWindow import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -42,7 +41,6 @@ class ScreenshotWindow @AssistedInject constructor( private val windowManager: WindowManager, - private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, private val context: Context, @Assisted private val display: Display, ) { @@ -97,7 +95,7 @@ constructor( Log.d(TAG, "attachWindow") } attachRequested = true - viewCaptureAwareWindowManager.addView(decorView, params) + windowManager.addView(decorView, params) decorView.requestApplyInsets() decorView.requireViewById<ViewGroup>(R.id.content).apply { @@ -135,7 +133,7 @@ constructor( if (LogConfig.DEBUG_WINDOW) { Log.d(TAG, "Removing screenshot window") } - viewCaptureAwareWindowManager.removeViewImmediate(decorView) + windowManager.removeViewImmediate(decorView) detachRequested = false } if (attachRequested && !detachRequested) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index c671f7d9db14..4ad4ab3410d6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -102,6 +102,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import dalvik.annotation.optimization.NeverCompile; @@ -299,6 +300,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum private final Runnable mQsCollapseExpandAction = this::collapseOrExpandQs; private final QS.ScrollListener mQsScrollListener = this::onScroll; + private final WindowManagerProvider mWindowManagerProvider; + @Inject public QuickSettingsControllerImpl( Lazy<NotificationPanelViewController> panelViewControllerLazy, @@ -336,7 +339,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum CastController castController, SplitShadeStateController splitShadeStateController, Lazy<CommunalTransitionViewModel> communalTransitionViewModelLazy, - Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy + Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy, + WindowManagerProvider windowManagerProvider ) { SceneContainerFlag.assertInLegacyMode(); mPanelViewControllerLazy = panelViewControllerLazy; @@ -387,6 +391,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback()); dumpManager.registerDumpable(this); + + mWindowManagerProvider = windowManagerProvider; } @VisibleForTesting @@ -532,7 +538,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum * on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events. */ void updateGestureInsetsCache() { - WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class); + WindowManager wm = mWindowManagerProvider.getWindowManager(this.mPanelView.getContext()); WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets( WindowInsets.Type.systemGestures()); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index cd224735cc62..446d4b450edc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -49,6 +49,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackRebind import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.utils.windowmanager.WindowManagerProvider +import com.android.systemui.utils.windowmanager.WindowManagerUtils import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey @@ -111,9 +113,10 @@ object ShadeDisplayAwareModule { fun provideShadeWindowManager( defaultWindowManager: WindowManager, @ShadeDisplayAware context: Context, + windowManagerProvider: WindowManagerProvider ): WindowManager { return if (ShadeWindowGoesAround.isEnabled) { - context.getSystemService(WindowManager::class.java) as WindowManager + windowManagerProvider.getWindowManager(context) } else { defaultWindowManager } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 88242762da78..c264e3525026 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -276,15 +276,17 @@ constructor( data object Weak : HeaderChipHighlight { override fun backgroundColor(colorScheme: ColorScheme): Color = - colorScheme.primary.copy(alpha = 0.1f) + colorScheme.surface.copy(alpha = 0.1f) - override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary + override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSurface } data object Strong : HeaderChipHighlight { - override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.secondary + override fun backgroundColor(colorScheme: ColorScheme): Color = + colorScheme.primaryContainer - override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSecondary + override fun foregroundColor(colorScheme: ColorScheme): Color = + colorScheme.onPrimaryContainer } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt index cf3b08c041be..8d2d9efb56eb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt @@ -71,6 +71,8 @@ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> { Swipe.Down to ShowOverlay(Overlays.NotificationsShade), Swipe.Down(fromSource = SceneContainerArea.EndHalf) to ShowOverlay(Overlays.QuickSettingsShade), + Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to + ShowOverlay(Overlays.QuickSettingsShade), ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java index 97e62d79b374..2a9a47d83dd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java @@ -28,9 +28,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; -import static com.android.systemui.Flags.enableViewCaptureTracing; -import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy; - import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.NonNull; @@ -76,16 +73,13 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.RelativeLayout; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.res.R; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.util.settings.SecureSettings; - -import kotlin.Lazy; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import javax.inject.Inject; @@ -112,13 +106,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca private long mShowDelayMs = 0L; private final IBinder mWindowToken = new Binder(); private final CommandQueue mCommandQueue; + private final WindowManagerProvider mWindowManagerProvider; private ClingWindowView mClingWindow; /** The wrapper on the last {@link WindowManager} used to add the confirmation window. */ @Nullable - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private WindowManager mWindowManager; /** - * The WindowContext that is registered with {@link #mViewCaptureAwareWindowManager} with + * The WindowContext that is registered with {@link #mWindowManager} with * options to specify the {@link RootDisplayArea} to attach the confirmation window. */ @Nullable @@ -136,21 +131,18 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca private ContentObserver mContentObserver; - private Lazy<ViewCapture> mLazyViewCapture; - @Inject public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue, - SecureSettings secureSettings, - dagger.Lazy<ViewCapture> daggerLazyViewCapture, - @Background Handler backgroundHandler) { + SecureSettings secureSettings, @Background Handler backgroundHandler, + WindowManagerProvider windowManagerProvider) { mSysUiContext = context; final Display display = mSysUiContext.getDisplay(); mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY ? mSysUiContext : mSysUiContext.createDisplayContext(display); mCommandQueue = commandQueue; mSecureSettings = secureSettings; - mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture); mBackgroundHandler = backgroundHandler; + mWindowManagerProvider = windowManagerProvider; } boolean loadSetting(int currentUserId) { @@ -257,14 +249,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca private void handleHide() { if (mClingWindow != null) { if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation"); - if (mViewCaptureAwareWindowManager != null) { + if (mWindowManager != null) { try { - mViewCaptureAwareWindowManager.removeView(mClingWindow); + mWindowManager.removeView(mClingWindow); } catch (WindowManager.InvalidDisplayException e) { Log.w(TAG, "Fail to hide the immersive confirmation window because of " + e); } - mViewCaptureAwareWindowManager = null; + mWindowManager = null; mWindowContext = null; } mClingWindow = null; @@ -525,8 +517,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca * confirmation window. */ @NonNull - private ViewCaptureAwareWindowManager createWindowManager(int rootDisplayAreaId) { - if (mViewCaptureAwareWindowManager != null) { + private WindowManager createWindowManager(int rootDisplayAreaId) { + if (mWindowManager != null) { throw new IllegalStateException( "Must not create a new WindowManager while there is an existing one"); } @@ -535,10 +527,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca mWindowContextRootDisplayAreaId = rootDisplayAreaId; mWindowContext = mDisplayContext.createWindowContext( IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options); - WindowManager wm = mWindowContext.getSystemService(WindowManager.class); - mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(wm, mLazyViewCapture, - enableViewCaptureTracing()); - return mViewCaptureAwareWindowManager; + mWindowManager = mWindowManagerProvider.getWindowManager(mWindowContext); + return mWindowManager; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index ef0660fbcd1c..3d7d08910502 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -79,6 +79,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetDialog; @@ -148,43 +149,44 @@ public final class KeyboardShortcutListSearch { private KeyCharacterMap mBackupKeyCharacterMap; @VisibleForTesting - KeyboardShortcutListSearch(Context context, WindowManager windowManager, int deviceId) { + KeyboardShortcutListSearch(Context context, @NonNull WindowManager windowManager, + int deviceId) { this.mContext = new ContextThemeWrapper( context, R.style.KeyboardShortcutHelper); this.mPackageManager = AppGlobals.getPackageManager(); - if (windowManager != null) { - this.mWindowManager = windowManager; - } else { - this.mWindowManager = mContext.getSystemService(WindowManager.class); - } + this.mWindowManager = windowManager; loadResources(this.mContext); createHardcodedShortcuts(deviceId); } - private static KeyboardShortcutListSearch getInstance(Context context, int deviceId) { + private static KeyboardShortcutListSearch getInstance(Context context, int deviceId, + WindowManagerProvider windowManagerProvider) { if (sInstance == null) { - sInstance = new KeyboardShortcutListSearch(context, null, deviceId); + WindowManager windowManager = windowManagerProvider.getWindowManager(context); + sInstance = new KeyboardShortcutListSearch(context, windowManager, deviceId); } return sInstance; } - public static void show(Context context, int deviceId) { + public static void show(Context context, int deviceId, + WindowManagerProvider windowManagerProvider) { MetricsLogger.visible(context, MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER); synchronized (sLock) { if (sInstance != null && !sInstance.mContext.equals(context)) { dismiss(); } - getInstance(context, deviceId).showKeyboardShortcuts(deviceId); + getInstance(context, deviceId, windowManagerProvider).showKeyboardShortcuts(deviceId); } } - public static void toggle(Context context, int deviceId) { + public static void toggle(Context context, int deviceId, + WindowManagerProvider windowManagerProvider) { synchronized (sLock) { if (isShowing()) { dismiss(); } else { - show(context, deviceId); + show(context, deviceId, windowManagerProvider); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 2157d754ce87..bd6006c8faa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -70,6 +70,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.Utils; import com.android.systemui.res.R; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import java.util.ArrayList; import java.util.Collections; @@ -141,38 +142,38 @@ public final class KeyboardShortcuts { this.mContext = new ContextThemeWrapper( context, android.R.style.Theme_DeviceDefault_Settings); this.mPackageManager = AppGlobals.getPackageManager(); - if (windowManager != null) { - this.mWindowManager = windowManager; - } else { - this.mWindowManager = mContext.getSystemService(WindowManager.class); - } + this.mWindowManager = windowManager; loadResources(context); } - private static KeyboardShortcuts getInstance(Context context) { + private static KeyboardShortcuts getInstance(Context context, + WindowManagerProvider windowManagerProvider) { if (sInstance == null) { - sInstance = new KeyboardShortcuts(context, null); + WindowManager windowManager = windowManagerProvider.getWindowManager(context); + sInstance = new KeyboardShortcuts(context, windowManager); } return sInstance; } - public static void show(Context context, int deviceId) { + public static void show(Context context, int deviceId, + WindowManagerProvider windowManagerProvider) { MetricsLogger.visible(context, MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER); synchronized (sLock) { if (sInstance != null && !sInstance.mContext.equals(context)) { dismiss(); } - getInstance(context).showKeyboardShortcuts(deviceId); + getInstance(context, windowManagerProvider).showKeyboardShortcuts(deviceId); } } - public static void toggle(Context context, int deviceId) { + public static void toggle(Context context, int deviceId, + WindowManagerProvider windowManagerProvider) { synchronized (sLock) { if (isShowing()) { dismiss(); } else { - show(context, deviceId); + show(context, deviceId, windowManagerProvider); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java index 815f1fcfdec6..54c84aa139cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java @@ -24,6 +24,7 @@ import android.content.Intent; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import javax.inject.Inject; @@ -31,10 +32,13 @@ import javax.inject.Inject; public class KeyboardShortcutsReceiver extends BroadcastReceiver { private final FeatureFlags mFeatureFlags; + private final WindowManagerProvider mWindowManagerProvider; @Inject - public KeyboardShortcutsReceiver(FeatureFlags featureFlags) { + public KeyboardShortcutsReceiver(FeatureFlags featureFlags, + WindowManagerProvider windowManagerProvider) { mFeatureFlags = featureFlags; + mWindowManagerProvider = windowManagerProvider; } @Override @@ -44,13 +48,14 @@ public class KeyboardShortcutsReceiver extends BroadcastReceiver { } if (isTabletLayoutFlagEnabled() && Utilities.isLargeScreen(context)) { if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { - KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */); + KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */, + mWindowManagerProvider); } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { KeyboardShortcutListSearch.dismiss(); } } else { if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { - KeyboardShortcuts.show(context, -1 /* deviceId unknown */); + KeyboardShortcuts.show(context, -1 /* deviceId unknown */, mWindowManagerProvider); } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { KeyboardShortcuts.dismiss(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt index 2d9ccb7b09b0..33ff6c014125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.mediaprojection.ui.view import android.app.ActivityManager import android.content.DialogInterface import android.content.pm.PackageManager +import android.util.Log import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.mediaprojection.data.model.MediaProjectionState @@ -54,10 +55,6 @@ constructor( // dialog to animate back into the chip just for the chip to disappear in a few frames. dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() stopAction.invoke() - // TODO(b/332662551): If the projection is stopped, there's a brief moment where the - // dialog closes and the chip re-shows because the system APIs haven't come back and - // told SysUI that the projection has officially stopped. It would be great for the chip - // to not re-show at all. } } @@ -85,8 +82,17 @@ constructor( val appInfo = packageManager.getApplicationInfo(packageName, 0) appInfo.loadLabel(packageManager) } catch (e: PackageManager.NameNotFoundException) { - // TODO(b/332662551): Log this error. + Log.w( + TAG, + "Failed to find application info for package: $packageName when creating " + + "end media projection dialog", + e, + ) null } } + + companion object { + private const val TAG = "EndMediaProjectionDialogHelper" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt index 9380dfe32bf5..0560c9e1f774 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt @@ -145,7 +145,9 @@ constructor( /** * Emits all notifications that are eligible to show as chips in the status bar. This is - * different from which chips will *actually* show, see [shownNotificationChips] for that. + * different from which chips will *actually* show, because + * [com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel] will + * hide chips that have [NotificationChipModel.isAppVisible] as true. */ val allNotificationChips: Flow<List<NotificationChipModel>> = if (StatusBarNotifChips.isEnabled) { @@ -186,15 +188,6 @@ constructor( initialValue = emptyList(), ) - /** Emits the notifications that should actually be *shown* as chips in the status bar. */ - val shownNotificationChips: Flow<List<NotificationChipModel>> = - allNotificationChips.map { chipsList -> - // If the app that posted this notification is visible, we want to hide the chip - // because information between the status bar chip and the app itself could be - // out-of-sync (like a timer that's slightly off) - chipsList.filter { !it.isAppVisible } - } - /* Stable sort the promoted notifications by two criteria: Criteria #1: Whichever app was most recently visible has higher ranking. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt index dfbd12d5a215..e45524b59837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -55,12 +55,12 @@ constructor( private val systemClock: SystemClock, ) { /** - * A flow modeling the notification chips that should be shown. Emits an empty list if there are - * no notifications that should show a status bar chip. + * A flow modeling the current notification chips. Emits an empty list if there are no + * notifications that are eligible to show a status bar chip. */ val chips: Flow<List<OngoingActivityChipModel.Active>> = combine( - notifChipsInteractor.shownNotificationChips, + notifChipsInteractor.allNotificationChips, headsUpNotificationInteractor.statusBarHeadsUpState, ) { notifications, headsUpState -> notifications.map { it.toActivityChipModel(headsUpState) } @@ -98,6 +98,10 @@ constructor( notifChipsInteractor.onPromotedNotificationChipTapped(this@toActivityChipModel.key) } } + // If the app that posted this notification is visible, we want to hide the chip + // because information between the status bar chip and the app itself could be + // out-of-sync (like a timer that's slightly off) + val isHidden = this.isAppVisible val onClickListenerLegacy = View.OnClickListener { StatusBarChipsModernization.assertInLegacyMode() @@ -122,6 +126,7 @@ constructor( colors = colors, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } @@ -133,6 +138,7 @@ constructor( text = chipContent.shortCriticalText, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } @@ -147,6 +153,7 @@ constructor( colors = colors, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } @@ -157,6 +164,7 @@ constructor( colors = colors, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } @@ -173,6 +181,7 @@ constructor( time = chipContent.time.currentTimeMillis, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } else { // Don't show a `when` time that's close to now or in the past because it's @@ -189,6 +198,7 @@ constructor( colors = colors, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } } @@ -201,6 +211,7 @@ constructor( isEventInFuture = chipContent.time.isCountDown, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, + isHidden = isHidden, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt index b2683762cb2a..fe6065d0bb42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt @@ -46,8 +46,9 @@ sealed interface ColorsModel { } /** The chip should match the system theme main color. */ - // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it - // only gets updated when a different configuration change happens, like a rotation. + // Note: When StatusBarChipsModernization is disabled, the chip's color doesn't get + // updated when the user switches theme. It only gets updated when a different + // configuration change happens, like a rotation. data object SystemThemed : ColorsModel { override fun background(context: Context): ColorStateList = ColorStateList.valueOf( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index e28f3684b0fa..ba60e2ce2aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -235,7 +235,6 @@ sealed class OngoingActivityChipModel { override val isImportantForPrivacy: Boolean = false, override val icon: ChipIcon, override val colors: ColorsModel, - // TODO(b/361346412): Enforce a max length requirement? val text: String, override val onClickListenerLegacy: View.OnClickListener? = null, override val clickBehavior: ClickBehavior, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt index 7fc5e8abe904..587d80fc3848 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.data.repository import android.view.Display import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -48,7 +47,6 @@ constructor( private val windowControllerFactory: PrivacyDotWindowController.Factory, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, private val privacyDotViewControllerStore: PrivacyDotViewControllerStore, - private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory, ) : PrivacyDotWindowControllerStore, StatusBarPerDisplayStoreImpl<PrivacyDotWindowController>( @@ -72,8 +70,7 @@ constructor( return windowControllerFactory.create( displayId = displayId, privacyDotViewController = privacyDotViewController, - viewCaptureAwareWindowManager = - viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager), + windowManager = displayWindowProperties.windowManager, inflater = displayWindowProperties.layoutInflater, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt index e2bcfb752e6c..7999ca9fd3e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt @@ -24,10 +24,10 @@ import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT import android.view.DisplayCutout.BOUNDS_POSITION_TOP import android.view.LayoutInflater import android.view.View +import android.view.WindowManager import android.view.WindowManager.InvalidDisplayException import android.view.WindowManager.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.ScreenDecorations import com.android.systemui.ScreenDecorationsThread import com.android.systemui.decor.DecorProvider @@ -54,7 +54,7 @@ class PrivacyDotWindowController constructor( @Assisted private val displayId: Int, @Assisted private val privacyDotViewController: PrivacyDotViewController, - @Assisted private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + @Assisted private val windowManager: WindowManager, @Assisted private val inflater: LayoutInflater, @ScreenDecorationsThread private val uiExecutor: Executor, private val dotFactory: PrivacyDotDecorProviderFactory, @@ -106,7 +106,7 @@ constructor( try { // Wrapping this in a try/catch to avoid crashes when a display is instantly removed // after being added, and initialization hasn't finished yet. - viewCaptureAwareWindowManager.addView(rootView, params) + windowManager.addView(rootView, params) } catch (e: InvalidDisplayException) { Log.e( TAG, @@ -118,7 +118,7 @@ constructor( } fun stop() { - dotViews.forEach { viewCaptureAwareWindowManager.removeView(it) } + dotViews.forEach { windowManager.removeView(it) } } @AssistedFactory @@ -126,7 +126,7 @@ constructor( fun create( displayId: Int, privacyDotViewController: PrivacyDotViewController, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, inflater: LayoutInflater, ): PrivacyDotWindowController } 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 ba80f016cad4..4c7c46dfae94 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 @@ -51,7 +51,7 @@ import java.util.Objects; */ public class HybridConversationNotificationView extends HybridNotificationView { - private static final int MAX_SUMMARIZATION_LINES = 2; + private static final int MAX_SUMMARIZATION_LINES = 1; private ImageView mConversationIconView; private TextView mConversationSenderName; private ViewStub mConversationFacePileStub; @@ -295,7 +295,6 @@ public class HybridConversationNotificationView extends HybridNotificationView { if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return; if (!TextUtils.isEmpty(summarization)) { mConversationSenderName.setVisibility(GONE); - titleText = null; contentText = summarization; mTextView.setSingleLine(false); mTextView.setMaxLines(MAX_SUMMARIZATION_LINES); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index e617254fa288..fe367a3927ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -90,7 +90,6 @@ import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleRegistry; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.MetricsLogger; @@ -233,6 +232,7 @@ import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.MessageRouter; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.bubbles.Bubbles; @@ -597,7 +597,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory; - private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final QuickAccessWalletController mWalletController; /** @@ -713,8 +712,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { BrightnessMirrorShowingRepository brightnessMirrorShowingRepository, GlanceableHubContainerController glanceableHubContainerController, EmergencyGestureIntentFactory emergencyGestureIntentFactory, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, - QuickAccessWalletController walletController + QuickAccessWalletController walletController, + WindowManager windowManager, + WindowManagerProvider windowManagerProvider ) { mContext = context; mNotificationsController = notificationsController; @@ -852,7 +852,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLightRevealScrim = lightRevealScrim; - mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; + mWindowManagerProvider = windowManagerProvider; } private void initBubbles(Bubbles bubbles) { @@ -880,8 +881,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - mDisplay = mContext.getDisplay(); mDisplayId = mDisplay.getDisplayId(); updateDisplaySize(); @@ -1716,7 +1715,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mNotificationShadeWindowController.setRequestTopUi(false, TAG); } }, /* isDozing= */ false, RippleShape.CIRCLE, - sUiEventLogger, mViewCaptureAwareWindowManager).show(animationDelay); + sUiEventLogger, mWindowManager, mWindowManagerProvider).show(animationDelay); } @Override @@ -2876,6 +2875,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected WindowManager mWindowManager; protected IWindowManager mWindowManagerService; private final IDreamManager mDreamManager; + private final WindowManagerProvider mWindowManagerProvider; protected Display mDisplay; private int mDisplayId; @@ -2912,9 +2912,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected void toggleKeyboardShortcuts(int deviceId) { if (shouldUseTabletKeyboardShortcuts()) { - KeyboardShortcutListSearch.toggle(mContext, deviceId); + KeyboardShortcutListSearch.toggle(mContext, deviceId, mWindowManagerProvider); } else { - KeyboardShortcuts.toggle(mContext, deviceId); + KeyboardShortcuts.toggle(mContext, deviceId, mWindowManagerProvider); } } @@ -2928,7 +2928,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private boolean shouldUseTabletKeyboardShortcuts() { return mFeatureFlags.isEnabled(SHORTCUT_LIST_SEARCH_LAYOUT) - && Utilities.isLargeScreen(mContext); + && Utilities.isLargeScreen(mWindowManager, mContext.getResources()); } private void clearNotificationEffects() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index caf8a43b2aaf..67a7eee2977a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -104,6 +104,7 @@ private constructor( // intercepted. See [View.OnTouchEvent] if (event.source == InputDevice.SOURCE_MOUSE) { if (event.action == MotionEvent.ACTION_UP) { + dispatchEventToShadeDisplayPolicy(event) v.performClick() shadeController.animateExpandShade() } @@ -113,6 +114,15 @@ private constructor( } } + private fun dispatchEventToShadeDisplayPolicy(event: MotionEvent) { + if (ShadeWindowGoesAround.isEnabled) { + // Notify the shade display policy that the status bar was touched. This may cause + // the shade to change display if the touch was in a display different than the shade + // one. + lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width) + } + } + private val configurationListener = object : ConfigurationController.ConfigurationListener { override fun onDensityOrFontScaleChanged() { @@ -232,9 +242,6 @@ private constructor( !upOrCancel || shadeController.isExpandedVisible, ) } - if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) { - lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width) - } } private fun addDarkReceivers() { @@ -249,6 +256,9 @@ private constructor( inner class PhoneStatusBarViewTouchHandler : Gefingerpoken { override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_DOWN) { + dispatchEventToShadeDisplayPolicy(event) + } return if (Flags.statusBarSwipeOverChip()) { shadeViewController.handleExternalInterceptTouch(event) } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java index fa022b4768fc..0f8d534df659 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java @@ -28,7 +28,6 @@ import android.util.IndentingPrintWriter; import androidx.annotation.NonNull; import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -49,7 +48,7 @@ public final class DeviceStateRotationLockSettingController private final RotationPolicyWrapper mRotationPolicyWrapper; private final DeviceStateManager mDeviceStateManager; private final Executor mMainExecutor; - private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager; + private final DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingManager; private final DeviceStateRotationLockSettingControllerLogger mLogger; // On registration for DeviceStateCallback, we will receive a callback with the current state @@ -65,13 +64,13 @@ public final class DeviceStateRotationLockSettingController RotationPolicyWrapper rotationPolicyWrapper, DeviceStateManager deviceStateManager, @Main Executor executor, - DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager, + DeviceStateAutoRotateSettingManager deviceStateAutoRotateSettingManager, DeviceStateRotationLockSettingControllerLogger logger, DumpManager dumpManager) { mRotationPolicyWrapper = rotationPolicyWrapper; mDeviceStateManager = deviceStateManager; mMainExecutor = executor; - mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager; + mDeviceStateAutoRotateSettingManager = deviceStateAutoRotateSettingManager; mLogger = logger; dumpManager.registerDumpable(this); } @@ -86,14 +85,14 @@ public final class DeviceStateRotationLockSettingController mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback); mDeviceStateAutoRotateSettingListener = () -> readPersistedSetting("deviceStateRotationLockChange", mDeviceState); - mDeviceStateRotationLockSettingsManager.registerListener( + mDeviceStateAutoRotateSettingManager.registerListener( mDeviceStateAutoRotateSettingListener); } else { if (mDeviceStateCallback != null) { mDeviceStateManager.unregisterCallback(mDeviceStateCallback); } if (mDeviceStateAutoRotateSettingListener != null) { - mDeviceStateRotationLockSettingsManager.unregisterListener( + mDeviceStateAutoRotateSettingManager.unregisterListener( mDeviceStateAutoRotateSettingListener); } } @@ -102,7 +101,7 @@ public final class DeviceStateRotationLockSettingController @Override public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) { int deviceState = mDeviceState; - boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager + boolean currentRotationLocked = mDeviceStateAutoRotateSettingManager .isRotationLocked(deviceState); mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked); if (deviceState == -1) { @@ -117,7 +116,7 @@ public final class DeviceStateRotationLockSettingController private void saveNewRotationLockSetting(boolean isRotationLocked) { int deviceState = mDeviceState; mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState); - mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked); + mDeviceStateAutoRotateSettingManager.updateSetting(deviceState, isRotationLocked); } private void updateDeviceState(@NonNull DeviceState state) { @@ -139,7 +138,7 @@ public final class DeviceStateRotationLockSettingController private void readPersistedSetting(String caller, int state) { int rotationLockSetting = - mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state); + mDeviceStateAutoRotateSettingManager.getRotationLockSetting(state); boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED; boolean isLocked = mRotationPolicyWrapper.isRotationLocked(); @@ -167,7 +166,7 @@ public final class DeviceStateRotationLockSettingController @Override public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter); - mDeviceStateRotationLockSettingsManager.dump(pw); + mDeviceStateAutoRotateSettingManager.dump(printWriter, null); pw.println("DeviceStateRotationLockSettingController"); pw.increaseIndent(); pw.println("mDeviceState: " + mDeviceState); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index d1e807f18196..e2a6c195af38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -18,13 +18,20 @@ package com.android.systemui.statusbar.policy.dagger; import android.content.Context; import android.content.res.Resources; +import android.hardware.devicestate.DeviceStateManager; +import android.os.Handler; import android.os.UserManager; import com.android.internal.R; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; +import com.android.settingslib.devicestate.AndroidSecureSettings; +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerProvider; +import com.android.settingslib.devicestate.PosturesHelper; +import com.android.settingslib.devicestate.SecureSettings; import com.android.settingslib.notification.modes.ZenIconLoader; 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.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.log.LogBuffer; @@ -222,12 +229,34 @@ public interface StatusBarPolicyModule { return controller; } - /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */ + /** */ + @SysUISingleton + @Provides + static SecureSettings provideAndroidSecureSettings(Context context) { + return new AndroidSecureSettings(context.getContentResolver()); + } + + /** */ @SysUISingleton @Provides - static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager( - Context context) { - return DeviceStateRotationLockSettingsManager.getInstance(context); + static PosturesHelper providePosturesHelper(Context context, + DeviceStateManager deviceStateManager) { + return new PosturesHelper(context, deviceStateManager); + } + + /** Returns a singleton instance of DeviceStateAutoRotateSettingManager based on auto-rotate + * refactor flag. */ + @SysUISingleton + @Provides + static DeviceStateAutoRotateSettingManager provideAutoRotateSettingsManager( + Context context, + @Background Executor bgExecutor, + SecureSettings secureSettings, + @Main Handler mainHandler, + PosturesHelper posturesHelper + ) { + return DeviceStateAutoRotateSettingManagerProvider.createInstance(context, bgExecutor, + secureSettings, mainHandler, posturesHelper); } /** 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 ecfcb29a9944..eae310ed169b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.window import android.content.Context import android.view.View import android.view.ViewGroup -import com.android.app.viewcapture.ViewCaptureAwareWindowManager +import android.view.WindowManager import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController @@ -84,7 +84,7 @@ interface StatusBarWindowController { fun interface Factory { fun create( context: Context, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, statusBarConfigurationController: StatusBarConfigurationController, contentInsetsProvider: StatusBarContentInsetsProvider, ): StatusBarWindowController 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 25972ac2bedf..77cd58111b94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java @@ -47,7 +47,6 @@ import android.view.WindowManager; import androidx.annotation.NonNull; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.animation.DelegateTransitionAnimatorController; @@ -78,7 +77,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController private static final boolean DEBUG = false; private final Context mContext; - private final ViewCaptureAwareWindowManager mWindowManager; + private final WindowManager mWindowManager; private final StatusBarConfigurationController mStatusBarConfigurationController; private final IWindowManager mIWindowManager; private final StatusBarContentInsetsProvider mContentInsetsProvider; @@ -100,7 +99,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController public StatusBarWindowControllerImpl( @Assisted Context context, @InternalWindowViewInflater StatusBarWindowViewInflater statusBarWindowViewInflater, - @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + @Assisted WindowManager windowManager, @Assisted StatusBarConfigurationController statusBarConfigurationController, IWindowManager iWindowManager, @Assisted StatusBarContentInsetsProvider contentInsetsProvider, @@ -108,7 +107,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, @Main Executor mainExecutor) { mContext = context; - mWindowManager = viewCaptureAwareWindowManager; + mWindowManager = windowManager; mStatusBarConfigurationController = statusBarConfigurationController; mIWindowManager = iWindowManager; mContentInsetsProvider = contentInsetsProvider; @@ -406,7 +405,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController @Override StatusBarWindowControllerImpl create( @NonNull Context context, - @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, + @NonNull WindowManager windowManager, @NonNull StatusBarConfigurationController statusBarConfigurationController, @NonNull StatusBarContentInsetsProvider contentInsetsProvider); } 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 39afc38dad11..74c028a8c7ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.window import android.content.Context import android.view.WindowManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository @@ -42,7 +41,6 @@ constructor( @Background backgroundApplicationScope: CoroutineScope, private val controllerFactory: StatusBarWindowController.Factory, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, - private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory, private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore, displayRepository: DisplayRepository, @@ -67,11 +65,9 @@ constructor( statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null val contentInsetsProvider = statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null - val viewCaptureAwareWindowManager = - viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager) return controllerFactory.create( statusBarDisplayContext.context, - viewCaptureAwareWindowManager, + statusBarDisplayContext.windowManager, statusBarConfigurationController, contentInsetsProvider, ) @@ -89,7 +85,7 @@ class SingleDisplayStatusBarWindowControllerStore @Inject constructor( context: Context, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, factory: StatusBarWindowControllerImpl.Factory, statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore, @@ -98,7 +94,7 @@ constructor( PerDisplayStore<StatusBarWindowController> by SingleDisplayStore( factory.create( context, - viewCaptureAwareWindowManager, + windowManager, statusBarConfigurationControllerStore.defaultDisplay, statusBarContentInsetsProviderStore.defaultDisplay, ) diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt index 635576743462..ea5f422f977d 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt @@ -32,7 +32,6 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.CoreStartable import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager @@ -70,7 +69,7 @@ import java.io.PrintWriter abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger<T>>( internal val context: Context, internal val logger: U, - internal val windowManager: ViewCaptureAwareWindowManager, + internal val windowManager: WindowManager, @Main private val mainExecutor: DelayableExecutor, private val accessibilityManager: AccessibilityManager, private val configurationController: ConfigurationController, diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt index 9b9cba9186f4..b6f54331b29f 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt @@ -29,6 +29,7 @@ import android.view.View import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE import android.view.ViewGroup +import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityNodeInfo import android.widget.ImageView @@ -37,7 +38,6 @@ import androidx.annotation.DimenRes import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.widget.CachingIconView import com.android.systemui.Gefingerpoken import com.android.systemui.classifier.FalsingCollector @@ -81,7 +81,7 @@ open class ChipbarCoordinator constructor( context: Context, logger: ChipbarLogger, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, @Main mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, @@ -100,7 +100,7 @@ constructor( TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>( context, logger, - viewCaptureAwareWindowManager, + windowManager, mainExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt index c11e4c507914..00c6ffd861da 100644 --- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt +++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt @@ -21,7 +21,6 @@ import android.graphics.PixelFormat import android.view.Gravity import android.view.WindowInsets import android.view.WindowManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -38,7 +37,7 @@ import javax.inject.Inject class TopLevelWindowEffects @Inject constructor( @Application private val context: Context, @Application private val applicationScope: CoroutineScope, - private val windowManager: ViewCaptureAwareWindowManager, + private val windowManager: WindowManager, private val squeezeEffectInteractor: SqueezeEffectInteractor, private val keyEventInteractor: KeyEventInteractor, private val viewModelFactory: SqueezeEffectViewModel.Factory diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt index 70fd5ab767d0..f1a556353273 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt +++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt @@ -20,7 +20,6 @@ import android.graphics.Rect import android.util.IndentingPrintWriter import android.view.View import android.view.ViewGroup -import dagger.Lazy import java.io.PrintWriter /** [Sequence] that yields all of the direct children of this [ViewGroup] */ @@ -56,11 +55,6 @@ val View.boundsOnScreen: Rect return bounds } -/** Extension method to convert [dagger.Lazy] to [kotlin.Lazy] for object of any class [T]. */ -fun <T> Lazy<T>.toKotlinLazy(): kotlin.Lazy<T> { - return lazy { this.get() } -} - /** * Returns whether this [Collection] contains exactly all [elements]. * diff --git a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java index 8acd6535e751..757b2d973312 100644 --- a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java +++ b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java @@ -21,6 +21,8 @@ import android.hardware.display.DisplayManager; import android.view.Display; import android.view.WindowManager; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; + import javax.inject.Inject; /** @@ -29,14 +31,17 @@ import javax.inject.Inject; public class DisplayHelper { private final Context mContext; private final DisplayManager mDisplayManager; + private final WindowManagerProvider mWindowManagerProvider; /** * Default constructor. */ @Inject - public DisplayHelper(Context context, DisplayManager displayManager) { + public DisplayHelper(Context context, DisplayManager displayManager, + WindowManagerProvider windowManagerProvider) { mContext = context; mDisplayManager = displayManager; + mWindowManagerProvider = windowManagerProvider; } @@ -45,9 +50,8 @@ public class DisplayHelper { */ public Rect getMaxBounds(int displayId, int windowContextType) { final Display display = mDisplayManager.getDisplay(displayId); - WindowManager windowManager = mContext.createDisplayContext(display) - .createWindowContext(windowContextType, null) - .getSystemService(WindowManager.class); + WindowManager windowManager = mWindowManagerProvider.getWindowManager(mContext + .createDisplayContext(display).createWindowContext(windowContextType, null)); return windowManager.getMaximumWindowMetrics().getBounds(); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt index 3ac6c7bc0c6b..457b6ac42f8b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog import android.content.Context import android.os.Bundle +import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.ViewGroup @@ -57,8 +58,10 @@ constructor( attributes.apply { title = "VolumeDialog" // Not the same as Window#setTitle } - setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT) + setGravity(Gravity.END) } + setCancelable(false) setCanceledOnTouchOutside(true) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt index 938e313771ad..b06a3b7784f8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt @@ -68,6 +68,7 @@ constructor( viewModel.isShowingSafetyWarning .mapLatest { isShowingSafetyWarning -> if (isShowingSafetyWarning) { + viewModel.onSafetyWarningDialogShown() showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() } } } @@ -76,6 +77,7 @@ constructor( viewModel.csdWarning .mapLatest { csdWarning -> if (csdWarning != null) { + viewModel.onCsdWarningDialogShown() showCsdWarningDialog(csdWarning, viewModel.csdWarningConfigModel.actions) { viewModel.onCsdWarningDismissed() } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt index bd23e8cce82d..4ed25859fb86 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt @@ -108,6 +108,7 @@ constructor( /** Resets current dialog timeout. */ fun resetDismissTimeout() { + controller.userActivity() mutableDismissDialogEvents.tryEmit(Unit) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt index fb9884cf4341..ad24f08a7214 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt @@ -111,7 +111,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( view.id, ConstraintSet.START, motionLayout.context.resources.getDimensionPixelSize( - R.dimen.volume_dialog_ringer_drawer_margin + R.dimen.volume_dialog_background_margin ), ) } @@ -121,7 +121,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( view.id, ConstraintSet.END, motionLayout.context.resources.getDimensionPixelSize( - R.dimen.volume_dialog_components_spacing + R.dimen.volume_dialog_ringer_drawer_buttons_spacing ), ) } else { @@ -140,7 +140,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( view.id, ConstraintSet.BOTTOM, motionLayout.context.resources.getDimensionPixelSize( - R.dimen.volume_dialog_components_spacing + R.dimen.volume_dialog_ringer_drawer_buttons_spacing ), ) } else { @@ -158,10 +158,10 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( R.dimen.volume_dialog_ringer_drawer_button_size ) * (motionLayout.childCount - 1)) + (motionLayout.context.resources.getDimensionPixelSize( - R.dimen.volume_dialog_ringer_drawer_margin + R.dimen.volume_dialog_background_margin ) * 2) + (motionLayout.context.resources.getDimensionPixelSize( - R.dimen.volume_dialog_components_spacing + R.dimen.volume_dialog_ringer_drawer_buttons_spacing ) * (motionLayout.childCount - 2)) ORIENTATION_PORTRAIT -> diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt index 6d16300bf56e..87cc1830af28 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt @@ -70,10 +70,18 @@ constructor( val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning val csdWarning: Flow<Int?> = dialogCsdWarningInteractor.csdWarning + fun onSafetyWarningDialogShown() { + dialogVisibilityInteractor.resetDismissTimeout() + } + fun onSafetyWarningDismissed() { dialogSafetyWarningInteractor.onSafetyWarningDismissed() } + fun onCsdWarningDialogShown() { + dialogVisibilityInteractor.resetDismissTimeout() + } + fun onCsdWarningDismissed() { dialogCsdWarningInteractor.onCsdWarningDismissed() } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index b1c6455a6e57..107f670eae8d 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -42,7 +42,6 @@ import android.service.wallpaper.WallpaperService; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; -import android.view.WindowManager; import androidx.annotation.NonNull; @@ -50,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -71,6 +71,7 @@ public class ImageWallpaper extends WallpaperService { private boolean mPagesComputed = false; private final UserTracker mUserTracker; + private final WindowManagerProvider mWindowManagerProvider; // used to handle WallpaperService messages (e.g. DO_ATTACH, MSG_UPDATE_SURFACE) // and to receive WallpaperService callbacks (e.g. onCreateEngine, onSurfaceRedrawNeeded) @@ -84,10 +85,12 @@ public class ImageWallpaper extends WallpaperService { private static final int DELAY_UNLOAD_BITMAP = 2000; @Inject - public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) { + public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker, + WindowManagerProvider windowManagerProvider) { super(); mLongExecutor = longExecutor; mUserTracker = userTracker; + mWindowManagerProvider = windowManagerProvider; } @Override @@ -552,8 +555,7 @@ public class ImageWallpaper extends WallpaperService { } private void getDisplaySizeAndUpdateColorExtractor() { - Rect window = getDisplayContext() - .getSystemService(WindowManager.class) + Rect window = mWindowManagerProvider.getWindowManager(getDisplayContext()) .getCurrentWindowMetrics() .getBounds(); mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 0769ada805a2..645e306eed07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -80,8 +80,6 @@ import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository; @@ -104,7 +102,6 @@ import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.events.PrivacyDotViewController; -import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.kotlin.JavaAdapter; @@ -112,8 +109,6 @@ import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; -import kotlin.Lazy; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -132,7 +127,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { private ScreenDecorations mScreenDecorations; private WindowManager mWindowManager; - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private DisplayManager mDisplayManager; private SecureSettings mSecureSettings; private FakeExecutor mExecutor; @@ -179,8 +173,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { private CutoutDecorProviderFactory mCutoutFactory; @Mock private JavaAdapter mJavaAdapter; - @Mock - private Lazy<ViewCapture> mLazyViewCapture; private FakeFacePropertyRepository mFakeFacePropertyRepository = new FakeFacePropertyRepository(); @@ -254,14 +246,12 @@ public class ScreenDecorationsTest extends SysuiTestCase { new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mFakeFacePropertyRepository)); - mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager, - mLazyViewCapture, false); mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader, - mViewCaptureAwareWindowManager, mMainHandler, mExecutor) { + mWindowManager, mMainHandler, mExecutor) { @Override public void start() { super.start(); @@ -1276,7 +1266,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader, - mViewCaptureAwareWindowManager, mMainHandler, mExecutor); + mWindowManager, mMainHandler, mExecutor); screenDecorations.start(); when(mContext.getDisplay()).thenReturn(mDisplay); when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java index 6d75c4ca3a38..d3d4e24001cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java @@ -43,13 +43,13 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.LauncherProxyService; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import org.junit.Before; import org.junit.Test; @@ -94,7 +94,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase { @Mock private IWindowManager mIWindowManager; @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private WindowManagerProvider mWindowManagerProvider; private IMagnificationConnection mIMagnificationConnection; private MagnificationImpl mMagnification; @@ -116,8 +116,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase { mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue, mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker, getContext().getSystemService(DisplayManager.class), - mA11yLogger, mIWindowManager, mAccessibilityManager, - mViewCaptureAwareWindowManager); + mA11yLogger, mIWindowManager, mAccessibilityManager, mWindowManagerProvider); mMagnification.mWindowMagnificationControllerSupplier = new FakeWindowMagnificationControllerSupplier( mContext.getSystemService(DisplayManager.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java index 8bfd2545ff2b..ae96e8fe7b8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java @@ -50,13 +50,13 @@ import android.view.accessibility.IMagnificationConnectionCallback; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.LauncherProxyService; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import org.junit.Before; import org.junit.Test; @@ -98,7 +98,7 @@ public class MagnificationTest extends SysuiTestCase { @Mock private IWindowManager mIWindowManager; @Mock - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; + private WindowManagerProvider mWindowManagerProvider; @Before public void setUp() throws Exception { @@ -132,8 +132,7 @@ public class MagnificationTest extends SysuiTestCase { mCommandQueue, mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager, - getContext().getSystemService(AccessibilityManager.class), - mViewCaptureAwareWindowManager); + getContext().getSystemService(AccessibilityManager.class), mWindowManagerProvider); mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier( mContext.getSystemService(DisplayManager.class), mWindowMagnificationController); mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index a0f5b2214f80..ac0378b093c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -51,8 +51,6 @@ import android.view.animation.AccelerateInterpolator; import android.window.InputTransferToken; import androidx.test.filters.LargeTest; - -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -112,8 +110,6 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SysUiState mSysUiState; @Mock SecureSettings mSecureSettings; - @Mock - ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private SpyWindowMagnificationController mController; private WindowMagnificationController mSpyController; private WindowMagnificationAnimationController mWindowMagnificationAnimationController; @@ -167,7 +163,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mSecureSettings, scvhSupplier, mSfVsyncFrameProvider, - mViewCaptureAwareWindowManager); + mWindowManager); mSpyController = mController.getSpyController(); } @@ -1023,7 +1019,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { SecureSettings secureSettings, Supplier<SurfaceControlViewHost> scvhSupplier, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, - ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) { + WindowManager windowManager) { super( context, handler, @@ -1033,7 +1029,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { callback, sysUiState, secureSettings, - scvhSupplier); + scvhSupplier, + windowManager); mSpyController = Mockito.mock(WindowMagnificationController.class); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 5b32b922d377..c8e0b14152fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -238,7 +238,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mWindowMagnifierCallback, mSysUiState, mSecureSettings, - scvhSupplier); + scvhSupplier, + mWindowManager); verify(mMirrorWindowControl).setWindowDelegate( any(MirrorWindowControl.MirrorWindowDelegate.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java index 45b9f4ad2322..dfc3ff2791b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java @@ -65,8 +65,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; @@ -74,8 +72,6 @@ import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarW import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Lazy; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -104,8 +100,6 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private WindowMagnificationSettingsCallback mWindowMagnificationSettingsCallback; - @Mock - private Lazy<ViewCapture> mLazyViewCapture; private TestableWindowManager mWindowManager; private WindowMagnificationSettings mWindowMagnificationSettings; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); @@ -123,6 +117,7 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { final WindowManager wm = mContext.getSystemService(WindowManager.class); mWindowManager = spy(new TestableWindowManager(wm)); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); + mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then( @@ -130,11 +125,9 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then( returnsSecondArg()); - ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager, - mLazyViewCapture, /* isViewCaptureEnabled= */ false); mWindowMagnificationSettings = new WindowMagnificationSettings(mContext, mWindowMagnificationSettingsCallback, mSfVsyncFrameProvider, - mSecureSettings, vwm); + mSecureSettings, mWindowManager); mSettingView = mWindowMagnificationSettings.getSettingView(); mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_slider); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index bc9d4c7fa0e6..b81cbe48b4ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -38,8 +38,6 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.bluetooth.HearingAidDeviceManager; @@ -51,8 +49,6 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Lazy; - import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -78,7 +74,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private Context mContextWrapper; private WindowManager mWindowManager; - private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private AccessibilityManager mAccessibilityManager; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private AccessibilityFloatingMenuController mController; @@ -93,8 +88,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock - private Lazy<ViewCapture> mLazyViewCapture; - @Mock private NavigationModeController mNavigationModeController; @Mock private HearingAidDeviceManager mHearingAidDeviceManager; @@ -110,8 +103,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { }; mWindowManager = mContext.getSystemService(WindowManager.class); - mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager, - mLazyViewCapture, /* isViewCaptureEnabled= */ false); mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mTestableLooper = TestableLooper.get(this); @@ -172,8 +163,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { enableAccessibilityFloatingMenuConfig(); mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, - mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController, mHearingAidDeviceManager); + mAccessibilityManager, mSecureSettings, mNavigationModeController, + mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserUnlocked(); @@ -200,8 +191,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { enableAccessibilityFloatingMenuConfig(); mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, - mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController, mHearingAidDeviceManager); + mAccessibilityManager, mSecureSettings, mNavigationModeController, + mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserSwitching(fakeUserId); @@ -215,8 +206,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { enableAccessibilityFloatingMenuConfig(); mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, - mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController, mHearingAidDeviceManager); + mAccessibilityManager, mSecureSettings, mNavigationModeController, + mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserUnlocked(); mKeyguardCallback.onKeyguardVisibilityChanged(true); @@ -362,18 +353,15 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private AccessibilityFloatingMenuController setUpController() { final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager = - new ViewCaptureAwareWindowManager(windowManager, mLazyViewCapture, - /* isViewCaptureEnabled= */ false); final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext); mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); final AccessibilityFloatingMenuController controller = new AccessibilityFloatingMenuController(mContextWrapper, windowManager, - viewCaptureAwareWindowManager, displayManager, mAccessibilityManager, - mTargetsObserver, mModeObserver, mHearingAidDeviceManager, - mKeyguardUpdateMonitor, mSecureSettings, displayTracker, - mNavigationModeController, new Handler(mTestableLooper.getLooper())); + displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver, + mHearingAidDeviceManager, mKeyguardUpdateMonitor, mSecureSettings, + displayTracker, mNavigationModeController, + new Handler(mTestableLooper.getLooper())); controller.init(); return controller; 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 6cc8238f2d09..dfb6afe8bd12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -352,6 +352,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { val controller = createController() val runner = underTest.createEphemeralRunner(controller) runner.onAnimationCancelled() + waitForIdleSync() runner.onAnimationStart( TRANSIT_NONE, emptyArray(), @@ -359,12 +360,13 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { emptyArray(), iCallback, ) - waitForIdleSync() + verify(controller).onTransitionAnimationCancelled() verify(controller, never()).onTransitionAnimationStart(anyBoolean()) verify(listener).onTransitionAnimationCancelled() verify(listener, never()).onTransitionAnimationStart() + verify(iCallback).onAnimationFinished() assertNull(runner.delegate) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index a1d038ad8554..a9e6a3ebdb30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -28,8 +28,6 @@ import android.view.WindowManager import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator @@ -62,7 +60,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.testKosmos import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.google.common.truth.Truth.assertThat -import dagger.Lazy import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -98,7 +95,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var inflater: LayoutInflater @Mock private lateinit var windowManager: WindowManager - @Mock private lateinit var lazyViewCapture: kotlin.Lazy<ViewCapture> @Mock private lateinit var accessibilityManager: AccessibilityManager @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @@ -160,11 +156,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { UdfpsControllerOverlay( context, inflater, - ViewCaptureAwareWindowManager( - windowManager, - lazyViewCapture, - isViewCaptureEnabled = false, - ), + windowManager, accessibilityManager, statusBarStateController, statusBarKeyguardViewManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 2c70249bcb06..bf79d11b2fb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -1472,7 +1472,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) .thenReturn("Direction") - kosmos.promptViewModel.onAnnounceAccessibilityHint( + kosmos.promptViewModel.onUpdateAccessibilityHint( obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), true, ) @@ -1497,7 +1497,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) .thenReturn("Direction") - kosmos.promptViewModel.onAnnounceAccessibilityHint( + kosmos.promptViewModel.onUpdateAccessibilityHint( obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), true, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt index 57b397cfca7e..034bab855faf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt @@ -23,13 +23,11 @@ import android.view.WindowManager import android.view.WindowMetrics import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.UiEventLogger +import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.res.R import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController @@ -42,12 +40,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -62,7 +60,6 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var windowMetrics: WindowMetrics - @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture> private val systemClock = FakeSystemClock() @Before @@ -71,9 +68,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { `when`(featureFlags.isEnabled(Flags.CHARGING_RIPPLE)).thenReturn(true) controller = WiredChargingRippleController( commandRegistry, batteryController, configurationController, - featureFlags, context, windowManager, - ViewCaptureAwareWindowManager(windowManager, - lazyViewCapture, isViewCaptureEnabled = false), systemClock, uiEventLogger) + featureFlags, context, windowManager, systemClock, uiEventLogger) rippleView.setupShader() controller.rippleView = rippleView // Replace the real ripple view with a mock instance controller.registerCallbacks() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 9b314f25e02b..41fdaa74e57b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -73,10 +73,10 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.RemoteAnimationTarget; import android.view.View; import android.view.ViewRootImpl; +import android.view.WindowManager; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.foldables.FoldGracePeriodProvider; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; @@ -175,7 +175,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock BroadcastDispatcher mBroadcastDispatcher; private @Mock DismissCallbackRegistry mDismissCallbackRegistry; private @Mock DumpManager mDumpManager; - private @Mock ViewCaptureAwareWindowManager mWindowManager; + private @Mock WindowManager mWindowManager; private @Mock IActivityManager mActivityManager; private @Mock ConfigurationController mConfigurationController; private @Mock PowerManager mPowerManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt index c7beb158c2de..add87686bc9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt @@ -20,8 +20,8 @@ import android.content.Context import android.os.Handler import android.os.PowerManager import android.view.ViewGroup +import android.view.WindowManager import android.view.accessibility.AccessibilityManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.ConfigurationController @@ -35,7 +35,7 @@ class FakeMediaTttChipControllerReceiver( commandQueue: CommandQueue, context: Context, logger: MediaTttReceiverLogger, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, @@ -53,7 +53,7 @@ class FakeMediaTttChipControllerReceiver( commandQueue, context, logger, - viewCaptureAwareWindowManager, + windowManager, mainExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 378dd452d030..1aa6ac67ec27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -31,8 +31,6 @@ import android.view.accessibility.AccessibilityManager import android.widget.ImageView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.InstanceId import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase @@ -75,8 +73,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var rippleController: MediaTttReceiverRippleController - @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture> - private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager private lateinit var commandQueueCallback: CommandQueue.Callbacks private lateinit var fakeAppIconDrawable: Drawable private lateinit var uiEventLoggerFake: UiEventLoggerFake @@ -114,18 +110,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { fakeWakeLockBuilder = WakeLockFake.Builder(context) fakeWakeLockBuilder.setWakeLock(fakeWakeLock) - viewCaptureAwareWindowManager = - ViewCaptureAwareWindowManager( - windowManager, - lazyViewCapture, - isViewCaptureEnabled = false, - ) controllerReceiver = FakeMediaTttChipControllerReceiver( commandQueue, context, logger, - viewCaptureAwareWindowManager, + windowManager, fakeExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt index c90ac5993c31..18de32e80da6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt @@ -34,7 +34,6 @@ import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.SysuiTestCase @@ -100,7 +99,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var vibratorHelper: VibratorHelper @Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler - @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture> private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder private lateinit var fakeWakeLock: WakeLockFake private lateinit var chipbarCoordinator: ChipbarCoordinator @@ -145,11 +143,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { ChipbarCoordinator( context, chipbarLogger, - ViewCaptureAwareWindowManager( - windowManager, - lazyViewCapture, - isViewCaptureEnabled = false, - ), + windowManager, fakeExecutor, accessibilityManager, configurationController, @@ -1476,7 +1470,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button) private fun ChipStateSender.getExpectedStateText( - otherDeviceName: String = OTHER_DEVICE_NAME + otherDeviceName: String = OTHER_DEVICE_NAME, ): String? { return this.getChipTextString(context, otherDeviceName).loadText(context) } @@ -1487,7 +1481,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { commandQueueCallback.updateMediaTapToTransferSenderDisplay( StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, - null, + null ) } @@ -1497,7 +1491,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() { commandQueueCallback.updateMediaTapToTransferSenderDisplay( StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, - null, + null ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt index b4db6da2000a..b169cc12f08a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt @@ -27,7 +27,6 @@ import android.view.ViewConfiguration import android.view.WindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.jank.Cuj import com.android.internal.util.LatencyTracker import com.android.systemui.SysuiTestCase @@ -64,7 +63,7 @@ class BackPanelControllerTest : SysuiTestCase() { private var triggerThreshold: Float = 0.0f private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop @Mock private lateinit var vibratorHelper: VibratorHelper - @Mock private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager + @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var latencyTracker: LatencyTracker private val interactionJankMonitor by lazy { kosmos.interactionJankMonitor } @@ -79,7 +78,7 @@ class BackPanelControllerTest : SysuiTestCase() { mBackPanelController = BackPanelController( context, - viewCaptureAwareWindowManager, + windowManager, ViewConfiguration.get(context), Handler.createAsync(testableLooper.looper), systemClock, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java index e4a4953063bb..e949c8a10c9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java @@ -62,7 +62,6 @@ import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.wifi.WifiUtils; @@ -162,7 +161,7 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase { @Mock InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback; @Mock - private ViewCaptureAwareWindowManager mWindowManager; + private WindowManager mWindowManager; @Mock private ToastFactory mToastFactory; @Mock @@ -234,9 +233,8 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase { mSubscriptionManager, mTelephonyManager, mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher, mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController, - mWindowManager, mToastFactory, mWorkerHandler, - mCarrierConfigTracker, mLocationController, mDialogTransitionAnimator, - mWifiStateWorker, mFlags); + mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker, + mLocationController, mDialogTransitionAnimator, mWifiStateWorker, mFlags); mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, mInternetDetailsContentController.mOnSubscriptionsChangedListener); mInternetDetailsContentController.onStart(mInternetDialogCallback, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java index 8045a13ff9be..07204ee814d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java @@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.google.android.material.bottomsheet.BottomSheetDialog; @@ -61,6 +62,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { @Mock private BottomSheetDialog mBottomSheetDialog; @Mock WindowManager mWindowManager; @Mock Handler mHandler; + @Mock WindowManagerProvider mWindowManagerProvider; @Before public void setUp() { @@ -77,7 +79,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { public void toggle_isShowingTrue_instanceShouldBeNull() { when(mBottomSheetDialog.isShowing()).thenReturn(true); - mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID); + mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider); assertThat(mKeyboardShortcutListSearch.sInstance).isNull(); } @@ -86,7 +88,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { public void toggle_isShowingFalse_showKeyboardShortcuts() { when(mBottomSheetDialog.isShowing()).thenReturn(false); - mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID); + mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider); verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt()); verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt()); @@ -96,7 +98,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { public void requestAppKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID); + mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider); ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); @@ -114,7 +116,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { public void requestImeKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID); + mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider); ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java index 2cb9791cc159..0bd9b29a1a37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java @@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import org.junit.After; import org.junit.Before; @@ -59,6 +60,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { @Mock private KeyboardShortcuts mKeyboardShortcuts; @Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch; + @Mock private WindowManagerProvider mWindowManagerProvider; @Before public void setUp() { @@ -69,7 +71,8 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { KeyboardShortcuts.sInstance = mKeyboardShortcuts; KeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch; - mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags)); + mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags, + mWindowManagerProvider)); } @Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java index 20ecaf75c625..939f2b899dbe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java @@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import org.junit.Before; import org.junit.Rule; @@ -67,6 +68,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Mock private Dialog mDialog; @Mock WindowManager mWindowManager; @Mock Handler mHandler; + @Mock WindowManagerProvider mWindowManagerProvider; @Before public void setUp() { @@ -92,7 +94,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { public void toggle_isShowingTrue_instanceShouldBeNull() { when(mDialog.isShowing()).thenReturn(true); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); assertThat(KeyboardShortcuts.sInstance).isNull(); } @@ -101,7 +103,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { public void toggle_isShowingFalse_showKeyboardShortcuts() { when(mDialog.isShowing()).thenReturn(false); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt()); verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt()); @@ -131,7 +133,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Test public void requestAppKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); emitAppShortcuts(singletonList(group), DEVICE_ID); @@ -142,7 +144,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Test public void requestImeKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); emitImeShortcuts(singletonList(group), DEVICE_ID); @@ -153,7 +155,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Test public void onImeAndAppShortcutsReceived_appShortcutsNull_doesNotCrash() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); emitImeShortcuts(singletonList(group), DEVICE_ID); emitAppShortcuts(/* groups= */ null, DEVICE_ID); @@ -162,7 +164,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Test public void onImeAndAppShortcutsReceived_imeShortcutsNull_doesNotCrash() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); emitAppShortcuts(singletonList(group), DEVICE_ID); emitImeShortcuts(/* groups= */ null, DEVICE_ID); @@ -170,7 +172,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Test public void onImeAndAppShortcutsReceived_bothNull_doesNotCrash() { - KeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider); emitImeShortcuts(/* groups= */ null, DEVICE_ID); emitAppShortcuts(/* groups= */ null, DEVICE_ID); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index a7f3fdcb517e..0c5cbc299aee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -93,7 +93,6 @@ import android.view.WindowMetrics; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.compose.animation.scene.ObservableTransitionState; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; @@ -214,6 +213,7 @@ import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SystemSettings; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.utils.windowmanager.WindowManagerProvider; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.bubbles.Bubbles; @@ -372,9 +372,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private GlanceableHubContainerController mGlanceableHubContainerController; @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory; @Mock private NotificationSettingsInteractor mNotificationSettingsInteractor; - @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector; @Mock private QuickAccessWalletController mQuickAccessWalletController; + @Mock private WindowManager mWindowManager; + @Mock private WindowManagerProvider mWindowManagerProvider; private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings(); @@ -642,8 +643,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mBrightnessMirrorShowingRepository, mGlanceableHubContainerController, mEmergencyGestureIntentFactory, - mViewCaptureAwareWindowManager, - mQuickAccessWalletController + mQuickAccessWalletController, + mWindowManager, + mWindowManagerProvider ); mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver); mCentralSurfaces.initShadeVisibilityListener(); @@ -1363,15 +1365,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase { private void switchToScreenSize(int widthDp, int heightDp) { WindowMetrics windowMetrics = Mockito.mock(WindowMetrics.class); - WindowManager windowManager = Mockito.mock(WindowManager.class); Configuration configuration = new Configuration(); configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT; mContext.getOrCreateTestableResources().overrideConfiguration(configuration); when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, widthDp, heightDp)); - when(windowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics); - mContext.addMockSystemService(WindowManager.class, windowManager); + when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 68f66611c981..574b2c010a37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -471,6 +471,51 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { } @Test + @EnableFlags(ShadeWindowGoesAround.FLAG_NAME) + fun onTouch_withMouseOnEndSideIcons_flagOn_propagatedToShadeDisplayPolicy() { + val view = createViewMock() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE) + + val statusContainer = view.requireViewById<View>(R.id.system_icons) + statusContainer.dispatchTouchEvent(event) + + verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any()) + } + + @Test + @EnableFlags(ShadeWindowGoesAround.FLAG_NAME) + fun onTouch_withMouseOnStartSideIcons_flagOn_propagatedToShadeDisplayPolicy() { + val view = createViewMock() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE) + + val statusContainer = view.requireViewById<View>(R.id.status_bar_start_side_content) + statusContainer.dispatchTouchEvent(event) + + verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any()) + } + + @Test + @DisableFlags(ShadeWindowGoesAround.FLAG_NAME) + fun onTouch_withMouseOnSystemIcons_flagOff_notPropagatedToShadeDisplayPolicy() { + val view = createViewMock() + InstrumentationRegistry.getInstrumentation().runOnMainSync { + controller = createAndInitController(view) + } + val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE) + + val statusContainer = view.requireViewById<View>(R.id.system_icons) + statusContainer.dispatchTouchEvent(event) + + verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any()) + } + + @Test fun shadeIsExpandedOnStatusIconMouseClick() { val view = createViewMock() InstrumentationRegistry.getInstrumentation().runOnMainSync { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt new file mode 100644 index 000000000000..5695df5c307d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt @@ -0,0 +1,69 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod + +import androidx.test.filters.SmallTest +import com.android.systemui.activated +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.launchKairosNetwork +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import org.mockito.Mockito + +@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class) +@SmallTest +class MobileConnectionKairosAdapterTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() { + + var job: Job? = null + val kairosNetwork = testScope.backgroundScope.launchKairosNetwork() + + override fun recreateRepo(): MobileConnectionRepository { + lateinit var adapter: MobileConnectionRepositoryKairosAdapter + job?.cancel() + Mockito.clearInvocations(telephonyManager) + job = + testScope.backgroundScope.launch { + kairosNetwork.activateSpec { + val repo = activated { + MobileConnectionRepositoryKairosImpl( + MobileConnectionRepositoryTest.SUB_1_ID, + context, + subscriptionModel.toState(), + MobileConnectionRepositoryTest.DEFAULT_NAME_MODEL, + MobileConnectionRepositoryTest.SEP, + connectivityManager, + telephonyManager, + systemUiCarrierConfig, + fakeBroadcastDispatcher, + mobileMappings, + testDispatcher, + logger, + tableLogger, + flags, + ) + } + adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig) + Unit + } + } + testScope.runCurrent() + return adapter + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt new file mode 100644 index 000000000000..0cb7c1eea268 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt @@ -0,0 +1,72 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.activated +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.launchKairosNetwork +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import org.junit.runner.RunWith +import org.mockito.Mockito + +@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class MobileConnectionRepositoryKairosAdapterTest : MobileConnectionRepositoryTest() { + + var job: Job? = null + val kairosNetwork = testScope.backgroundScope.launchKairosNetwork() + + override fun recreateRepo(): MobileConnectionRepository { + lateinit var adapter: MobileConnectionRepositoryKairosAdapter + job?.cancel() + Mockito.clearInvocations(telephonyManager) + job = + testScope.backgroundScope.launch { + kairosNetwork.activateSpec { + val repo = activated { + MobileConnectionRepositoryKairosImpl( + SUB_1_ID, + context, + subscriptionModel.toState(), + DEFAULT_NAME_MODEL, + SEP, + connectivityManager, + telephonyManager, + systemUiCarrierConfig, + fakeBroadcastDispatcher, + mobileMappings, + testDispatcher, + logger, + tableLogger, + flags, + ) + } + adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig) + Unit + } + } + testScope.runCurrent() // ensure the lateinit is set + return adapter + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt index ed8be9b253ab..2636195c0021 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt @@ -88,6 +88,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfigWithOverride +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo @@ -116,25 +117,49 @@ import org.mockito.kotlin.argumentCaptor @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest @RunWith(AndroidJUnit4::class) -class MobileConnectionRepositoryTest : SysuiTestCase() { - private lateinit var underTest: MobileConnectionRepositoryImpl +class MobileConnectionRepositoryImplTest : MobileConnectionRepositoryTest() { + override fun recreateRepo(): MobileConnectionRepository = + MobileConnectionRepositoryImpl( + SUB_1_ID, + context, + subscriptionModel, + DEFAULT_NAME_MODEL, + SEP, + connectivityManager, + telephonyManager, + systemUiCarrierConfig, + fakeBroadcastDispatcher, + mobileMappings, + testDispatcher, + logger, + tableLogger, + flags, + testScope.backgroundScope, + ) +} + +abstract class MobileConnectionRepositoryTest : SysuiTestCase() { + + abstract fun recreateRepo(): MobileConnectionRepository + + lateinit var underTest: MobileConnectionRepository - private val flags = + protected val flags = FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) } - @Mock private lateinit var connectivityManager: ConnectivityManager - @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var logger: MobileInputLogger - @Mock private lateinit var tableLogger: TableLogBuffer - @Mock private lateinit var context: Context + @Mock protected lateinit var connectivityManager: ConnectivityManager + @Mock protected lateinit var telephonyManager: TelephonyManager + @Mock protected lateinit var logger: MobileInputLogger + @Mock protected lateinit var tableLogger: TableLogBuffer + @Mock protected lateinit var context: Context - private val mobileMappings = FakeMobileMappingsProxy() - private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig()) + protected val mobileMappings = FakeMobileMappingsProxy() + protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig()) - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) + protected val testDispatcher = UnconfinedTestDispatcher() + protected val testScope = TestScope(testDispatcher) - private val subscriptionModel: MutableStateFlow<SubscriptionModel?> = + protected val subscriptionModel: MutableStateFlow<SubscriptionModel?> = MutableStateFlow( SubscriptionModel( subscriptionId = SUB_1_ID, @@ -144,28 +169,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { ) @Before - fun setUp() { + fun setUpBase() { MockitoAnnotations.initMocks(this) whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID) - underTest = - MobileConnectionRepositoryImpl( - SUB_1_ID, - context, - subscriptionModel, - DEFAULT_NAME_MODEL, - SEP, - connectivityManager, - telephonyManager, - systemUiCarrierConfig, - fakeBroadcastDispatcher, - mobileMappings, - testDispatcher, - logger, - tableLogger, - flags, - testScope.backgroundScope, - ) + underTest = recreateRepo() } @Test @@ -400,6 +408,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { fun carrierId_initialValueCaptured() = testScope.runTest { whenever(telephonyManager.simCarrierId).thenReturn(1234) + underTest = recreateRepo() var latest: Int? = null val job = underTest.carrierId.onEach { latest = it }.launchIn(this) @@ -430,6 +439,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { @Test fun carrierNetworkChange() = testScope.runTest { + underTest = recreateRepo() + var latest: Boolean? = null val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this) @@ -622,24 +633,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) // Re-create the repository, because the flag is read at init - underTest = - MobileConnectionRepositoryImpl( - SUB_1_ID, - context, - subscriptionModel, - DEFAULT_NAME_MODEL, - SEP, - connectivityManager, - telephonyManager, - systemUiCarrierConfig, - fakeBroadcastDispatcher, - mobileMappings, - testDispatcher, - logger, - tableLogger, - flags, - testScope.backgroundScope, - ) + underTest = recreateRepo() var latest: Boolean? = null val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) @@ -671,24 +665,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, false) // Re-create the repository, because the flag is read at init - underTest = - MobileConnectionRepositoryImpl( - SUB_1_ID, - context, - subscriptionModel, - DEFAULT_NAME_MODEL, - SEP, - connectivityManager, - telephonyManager, - systemUiCarrierConfig, - fakeBroadcastDispatcher, - mobileMappings, - testDispatcher, - logger, - tableLogger, - flags, - testScope.backgroundScope, - ) + underTest = recreateRepo() var latest: Boolean? = null val job = underTest.isRoaming.onEach { latest = it }.launchIn(this) @@ -1441,14 +1418,14 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { } companion object { - private const val SUB_1_ID = 1 + const val SUB_1_ID = 1 - private const val DEFAULT_NAME = "Fake Mobile Network" - private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME) - private const val SEP = "-" + const val DEFAULT_NAME = "Fake Mobile Network" + val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME) + const val SEP = "-" - private const val SPN = "testSpn" - private const val DATA_SPN = "testDataSpn" - private const val PLMN = "testPlmn" + const val SPN = "testSpn" + const val DATA_SPN = "testDataSpn" + const val PLMN = "testPlmn" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt index 6f21e795532b..caa6e21fd883 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetwork import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy @@ -92,47 +93,53 @@ import org.mockito.MockitoAnnotations */ @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @SmallTest -class MobileConnectionTelephonySmokeTests : SysuiTestCase() { - private lateinit var underTest: MobileConnectionRepositoryImpl +class MobileConnectionTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() { + override fun recreateRepo(): MobileConnectionRepository = + MobileConnectionRepositoryImpl( + SUB_1_ID, + context, + subscriptionModel, + DEFAULT_NAME, + SEP, + connectivityManager, + telephonyManager, + systemUiCarrierConfig, + fakeBroadcastDispatcher, + mobileMappings, + testDispatcher, + logger, + tableLogger, + flags, + testScope.backgroundScope, + ) +} + +abstract class MobileConnectionTelephonySmokeTestsBase : SysuiTestCase() { + protected lateinit var underTest: MobileConnectionRepository - private val flags = + protected val flags = FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) } - @Mock private lateinit var connectivityManager: ConnectivityManager - @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var logger: MobileInputLogger - @Mock private lateinit var tableLogger: TableLogBuffer - @Mock private lateinit var subscriptionModel: StateFlow<SubscriptionModel?> + @Mock protected lateinit var connectivityManager: ConnectivityManager + @Mock protected lateinit var telephonyManager: TelephonyManager + @Mock protected lateinit var logger: MobileInputLogger + @Mock protected lateinit var tableLogger: TableLogBuffer + @Mock protected lateinit var subscriptionModel: StateFlow<SubscriptionModel?> - private val mobileMappings = FakeMobileMappingsProxy() - private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig()) + protected val mobileMappings = FakeMobileMappingsProxy() + protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig()) - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) + protected val testDispatcher = UnconfinedTestDispatcher() + protected val testScope = TestScope(testDispatcher) + + abstract fun recreateRepo(): MobileConnectionRepository @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID) - underTest = - MobileConnectionRepositoryImpl( - SUB_1_ID, - context, - subscriptionModel, - DEFAULT_NAME, - SEP, - connectivityManager, - telephonyManager, - systemUiCarrierConfig, - fakeBroadcastDispatcher, - mobileMappings, - testDispatcher, - logger, - tableLogger, - flags, - testScope.backgroundScope, - ) + underTest = recreateRepo() } @Test @@ -329,9 +336,9 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() { } companion object { - private const val SUB_1_ID = 1 + const val SUB_1_ID = 1 - private val DEFAULT_NAME = NetworkNameModel.Default("default name") - private const val SEP = "-" + val DEFAULT_NAME = NetworkNameModel.Default("default name") + const val SEP = "-" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt new file mode 100644 index 000000000000..65849ae103c4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt @@ -0,0 +1,126 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod + +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.launchKairosNetwork +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeCarrierConfigRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairosAdapter +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import org.mockito.Mockito +import org.mockito.kotlin.mock + +@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class) +@SmallTest +// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper +// to run the callback and this makes the looper place nicely with TestScope etc. +@TestableLooper.RunWithLooper +class MobileConnectionsRepositoryKairosAdapterTest : + MobileConnectionsRepositoryTest<MobileConnectionsRepositoryKairosAdapter>() { + + var job: Job? = null + val kairosNetwork = testScope.backgroundScope.launchKairosNetwork() + + override fun recreateRepo(): MobileConnectionsRepositoryKairosAdapter { + val carrierConfigRepo = FakeCarrierConfigRepository() + lateinit var connectionsRepo: MobileConnectionsRepositoryKairosImpl + connectionsRepo = + MobileConnectionsRepositoryKairosImpl( + connectivityRepository = connectivityRepository, + subscriptionManager = subscriptionManager, + subscriptionManagerProxy = subscriptionManagerProxy, + telephonyManager = telephonyManager, + logger = logger, + tableLogger = summaryLogger, + mobileMappingsProxy = mobileMappings, + broadcastDispatcher = fakeBroadcastDispatcher, + context = context, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + airplaneModeRepository = airplaneModeRepository, + wifiRepository = wifiRepository, + keyguardUpdateMonitor = updateMonitor, + dumpManager = mock(), + mobileRepoFactory = { + MobileConnectionRepositoryKairosFactoryImpl( + context = context, + connectionsRepo = connectionsRepo, + logFactory = logBufferFactory, + carrierConfigRepo = carrierConfigRepo, + telephonyManager = telephonyManager, + mobileRepoFactory = { + subId, + mobileLogger, + subscriptionModel, + defaultNetworkName, + networkNameSeparator, + systemUiCarrierConfig, + telephonyManager -> + MobileConnectionRepositoryKairosImpl( + subId = subId, + context = context, + subscriptionModel = subscriptionModel, + defaultNetworkName = defaultNetworkName, + networkNameSeparator = networkNameSeparator, + connectivityManager = connectivityManager, + telephonyManager = telephonyManager, + systemUiCarrierConfig = systemUiCarrierConfig, + broadcastDispatcher = fakeBroadcastDispatcher, + mobileMappingsProxy = mobileMappings, + bgDispatcher = testDispatcher, + logger = logger, + tableLogBuffer = mobileLogger, + flags = flags, + ) + }, + mergedRepoFactory = + CarrierMergedConnectionRepositoryKairos.Factory( + telephonyManager, + wifiRepository, + ), + ) + }, + ) + + val adapter = + MobileConnectionsRepositoryKairosAdapter( + kairosRepo = connectionsRepo, + kairosNetwork = kairosNetwork, + scope = testScope.backgroundScope, + connectivityRepository = connectivityRepository, + context = context, + carrierConfigRepo = carrierConfigRepo, + ) + + job?.cancel() + Mockito.clearInvocations(telephonyManager) + job = + testScope.backgroundScope.launch { + kairosNetwork.activateSpec { + connectionsRepo.run { activate() } + adapter.run { activate() } + } + } + testScope.runCurrent() // ensure everything is activated + return adapter + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index d1d6e27332b0..c3662880c5cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -37,6 +37,7 @@ import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener +import android.telephony.TelephonyCallback.EmergencyCallbackModeListener import android.telephony.TelephonyManager import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -58,6 +59,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.carrierConfigRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy @@ -78,6 +80,7 @@ import com.android.wifitrackerlib.MergedCarrierEntry import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import com.google.common.truth.Truth.assertThat +import java.time.Duration import java.util.UUID import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn @@ -93,6 +96,7 @@ import org.junit.Test import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock +import org.mockito.Mockito.atLeast import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.any @@ -104,34 +108,311 @@ import org.mockito.kotlin.whenever // This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper // to run the callback and this makes the looper place nicely with TestScope etc. @TestableLooper.RunWithLooper -class MobileConnectionsRepositoryTest : SysuiTestCase() { +class MobileConnectionsRepositoryImplTest : + MobileConnectionsRepositoryTest<MobileConnectionsRepositoryImpl>() { + override fun recreateRepo() = + MobileConnectionsRepositoryImpl( + connectivityRepository = connectivityRepository, + subscriptionManager = subscriptionManager, + subscriptionManagerProxy = subscriptionManagerProxy, + telephonyManager = telephonyManager, + logger = logger, + tableLogger = summaryLogger, + mobileMappingsProxy = mobileMappings, + broadcastDispatcher = fakeBroadcastDispatcher, + context = context, + bgDispatcher = testDispatcher, + scope = testScope.backgroundScope, + mainDispatcher = testDispatcher, + airplaneModeRepository = airplaneModeRepository, + wifiRepository = wifiRepository, + fullMobileRepoFactory = fullConnectionFactory, + keyguardUpdateMonitor = updateMonitor, + dumpManager = mock(), + ) + + @Test + fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() = + testScope.runTest { + val activeRepo by collectLastValue(underTest.activeMobileDataRepository) + collectLastValue(underTest.subscriptions) + + // GIVEN active repo is updated before the subscription list updates + getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() + .onActiveDataSubscriptionIdChanged(SUB_2_ID) + + assertThat(activeRepo).isNotNull() + + // GIVEN the subscription list is then updated which includes the active data sub id + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_2)) + getSubscriptionCallback().onSubscriptionsChanged() + + // WHEN requesting a connection repository for the subscription + val newRepo = underTest.getRepoForSubId(SUB_2_ID) + + // THEN the newly request repo has been cached and reused + assertThat(activeRepo).isSameInstanceAs(newRepo) + } + + @Test + fun testConnectionRepository_invalidSubId_doesNotThrow() = + testScope.runTest { + underTest.getRepoForSubId(SUB_1_ID) + // No exception + } + + @Test + fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) + setWifiState(isCarrierMerged = true) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_CM)) + getSubscriptionCallback().onSubscriptionsChanged() + + val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) + val mobileRepo = underTest.getRepoForSubId(SUB_1_ID) + assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() + assertThat(mobileRepo.getIsCarrierMerged()).isFalse() + } + + @Test + fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) + setWifiState(isCarrierMerged = true) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_CM)) + getSubscriptionCallback().onSubscriptionsChanged() + + val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) + var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) + assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() + assertThat(mobileRepo.getIsCarrierMerged()).isFalse() + + // WHEN the wifi network updates to be not carrier merged + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) + setWifiState(isCarrierMerged = false) + runCurrent() + + // THEN the repos update + val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) + mobileRepo = underTest.getRepoForSubId(SUB_1_ID) + assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse() + assertThat(mobileRepo.getIsCarrierMerged()).isFalse() + } + + @Test + fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) + setWifiState(isCarrierMerged = false) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_CM)) + getSubscriptionCallback().onSubscriptionsChanged() + runCurrent() + + val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) + var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) + assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse() + assertThat(mobileRepo.getIsCarrierMerged()).isFalse() + + // WHEN the wifi network updates to be carrier merged + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) + setWifiState(isCarrierMerged = true) + runCurrent() + + // THEN the repos update + val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) + mobileRepo = underTest.getRepoForSubId(SUB_1_ID) + assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() + assertThat(mobileRepo.getIsCarrierMerged()).isFalse() + } + + @Test + @Ignore("b/333912012") + fun testConnectionCache_clearsInvalidSubscriptions() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_2)) + getSubscriptionCallback().onSubscriptionsChanged() + + // Get repos to trigger caching + val repo1 = underTest.getRepoForSubId(SUB_1_ID) + val repo2 = underTest.getRepoForSubId(SUB_2_ID) + + assertThat(underTest.getSubIdRepoCache()) + .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) + + // SUB_2 disappears + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1)) + getSubscriptionCallback().onSubscriptionsChanged() + + assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) + } + + @Test + @Ignore("b/333912012") + fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) + setWifiState(isCarrierMerged = true) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_2, SUB_CM)) + getSubscriptionCallback().onSubscriptionsChanged() + + // Get repos to trigger caching + val repo1 = underTest.getRepoForSubId(SUB_1_ID) + val repo2 = underTest.getRepoForSubId(SUB_2_ID) + val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID) + + assertThat(underTest.getSubIdRepoCache()) + .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged) + + // SUB_2 and SUB_CM disappear + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1)) + getSubscriptionCallback().onSubscriptionsChanged() + + assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) + } + + /** Regression test for b/261706421 */ + @Test + @Ignore("b/333912012") + fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() = + testScope.runTest { + collectLastValue(underTest.subscriptions) + + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1, SUB_2)) + getSubscriptionCallback().onSubscriptionsChanged() + + // Get repos to trigger caching + val repo1 = underTest.getRepoForSubId(SUB_1_ID) + val repo2 = underTest.getRepoForSubId(SUB_2_ID) + + assertThat(underTest.getSubIdRepoCache()) + .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) + + // All subscriptions disappear + whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) + getSubscriptionCallback().onSubscriptionsChanged() + + assertThat(underTest.getSubIdRepoCache()).isEmpty() + } + + @Test + fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() = + testScope.runTest { + var latestActiveRepo: MobileConnectionRepository? = null + collectLastValue( + underTest.activeMobileDataSubscriptionId.filterNotNull().onEach { + latestActiveRepo = underTest.getRepoForSubId(it) + } + ) + + val latestSubscriptions by collectLastValue(underTest.subscriptions) + + // Active data subscription id is sent, but no subscription change has been posted yet + getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() + .onActiveDataSubscriptionIdChanged(SUB_2_ID) + + // Subscriptions list is empty + assertThat(latestSubscriptions).isEmpty() + // getRepoForSubId does not throw + assertThat(latestActiveRepo).isNotNull() + } + + @Test + fun testConnectionsCache_keepsReposCached() = + testScope.runTest { + // Collect subscriptions to start the job + collectLastValue(underTest.subscriptions) + + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1)) + getSubscriptionCallback().onSubscriptionsChanged() + + val repo1_1 = underTest.getRepoForSubId(SUB_1_ID) + + // All subscriptions disappear + whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) + getSubscriptionCallback().onSubscriptionsChanged() + + // Sub1 comes back + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1)) + getSubscriptionCallback().onSubscriptionsChanged() + + val repo1_2 = underTest.getRepoForSubId(SUB_1_ID) + + assertThat(repo1_1).isSameInstanceAs(repo1_2) + } + + @Test + fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() = + testScope.runTest { + // Collect subscriptions to start the job + collectLastValue(underTest.subscriptions) + + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_1)) + getSubscriptionCallback().onSubscriptionsChanged() + + // Client grabs a reference to a repository, but doesn't keep it around + underTest.getRepoForSubId(SUB_1_ID) + + // All subscriptions disappear + whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) + getSubscriptionCallback().onSubscriptionsChanged() + + val repo1 = underTest.getRepoForSubId(SUB_1_ID) + + assertThat(repo1).isNotNull() + } +} + +abstract class MobileConnectionsRepositoryTest<T : MobileConnectionsRepository> : SysuiTestCase() { private val kosmos = testKosmos() - private val flags = + protected val flags = FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) } private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory - private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory - private lateinit var connectivityRepository: ConnectivityRepository - private lateinit var airplaneModeRepository: FakeAirplaneModeRepository - private lateinit var wifiRepository: WifiRepository + protected lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory + protected lateinit var connectivityRepository: ConnectivityRepository + protected lateinit var airplaneModeRepository: FakeAirplaneModeRepository + protected lateinit var wifiRepository: WifiRepository private lateinit var carrierConfigRepository: CarrierConfigRepository - @Mock private lateinit var connectivityManager: ConnectivityManager - @Mock private lateinit var subscriptionManager: SubscriptionManager - @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var logger: MobileInputLogger - private val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger") - @Mock private lateinit var logBufferFactory: TableLogBufferFactory - @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + @Mock protected lateinit var connectivityManager: ConnectivityManager + @Mock protected lateinit var subscriptionManager: SubscriptionManager + @Mock protected lateinit var telephonyManager: TelephonyManager + @Mock protected lateinit var logger: MobileInputLogger + protected val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger") + @Mock protected lateinit var logBufferFactory: TableLogBufferFactory + @Mock protected lateinit var updateMonitor: KeyguardUpdateMonitor @Mock private lateinit var wifiManager: WifiManager @Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory @Mock private lateinit var wifiPickerTracker: WifiPickerTracker private val wifiTableLogBuffer = logcatTableLogBuffer(kosmos, "wifiTableLog") - private val mobileMappings = FakeMobileMappingsProxy() - private val subscriptionManagerProxy = FakeSubscriptionManagerProxy() + protected val mobileMappings = FakeMobileMappingsProxy() + protected val subscriptionManagerProxy = FakeSubscriptionManagerProxy() private val mainExecutor = FakeExecutor(FakeSystemClock()) private val wifiLogBuffer = LogBuffer("wifi", maxSize = 100, logcatEchoTracker = mock()) private val wifiPickerTrackerCallback = @@ -139,10 +420,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { private val vcnTransportInfo = VcnTransportInfo.Builder().build() private val userRepository = kosmos.fakeUserRepository - private val testDispatcher = StandardTestDispatcher() - private val testScope = TestScope(testDispatcher) + protected val testDispatcher = StandardTestDispatcher() + protected val testScope = TestScope(testDispatcher) - private lateinit var underTest: MobileConnectionsRepositoryImpl + protected lateinit var underTest: T @Before fun setUp() { @@ -237,30 +518,13 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { carrierMergedRepoFactory = carrierMergedFactory, ) - underTest = - MobileConnectionsRepositoryImpl( - connectivityRepository, - subscriptionManager, - subscriptionManagerProxy, - telephonyManager, - logger, - summaryLogger, - mobileMappings, - fakeBroadcastDispatcher, - context, - /* bgDispatcher = */ testDispatcher, - testScope.backgroundScope, - /* mainDispatcher = */ testDispatcher, - airplaneModeRepository, - wifiRepository, - fullConnectionFactory, - updateMonitor, - mock(), - ) + underTest = recreateRepo() testScope.runCurrent() } + abstract fun recreateRepo(): T + @Test fun testSubscriptions_initiallyEmpty() = testScope.runTest { @@ -410,9 +674,17 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { fun activeRepo_updatesWithActiveDataId() = testScope.runTest { val latest by collectLastValue(underTest.activeMobileDataRepository) + runCurrent() - getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() - .onActiveDataSubscriptionIdChanged(SUB_2_ID) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_2)) + getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() } + runCurrent() + + getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach { + it.onActiveDataSubscriptionIdChanged(SUB_2_ID) + } + runCurrent() assertThat(latest?.subId).isEqualTo(SUB_2_ID) } @@ -422,6 +694,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.activeMobileDataRepository) + whenever(subscriptionManager.completeActiveSubscriptionInfoList) + .thenReturn(listOf(SUB_2)) + getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() } + getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() .onActiveDataSubscriptionIdChanged(SUB_2_ID) @@ -437,60 +713,15 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { /** Regression test for b/268146648. */ fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() = testScope.runTest { - val activeRepo by collectLastValue(underTest.activeMobileDataRepository) + val activeRepo = collectLastValue(underTest.activeMobileDataRepository) val subscriptions by collectLastValue(underTest.subscriptions) - getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() - .onActiveDataSubscriptionIdChanged(SUB_2_ID) + getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach { + it.onActiveDataSubscriptionIdChanged(SUB_2_ID) + } assertThat(subscriptions).isEmpty() - assertThat(activeRepo).isNotNull() - } - - @Test - fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() = - testScope.runTest { - var latestActiveRepo: MobileConnectionRepository? = null - collectLastValue( - underTest.activeMobileDataSubscriptionId.filterNotNull().onEach { - latestActiveRepo = underTest.getRepoForSubId(it) - } - ) - - val latestSubscriptions by collectLastValue(underTest.subscriptions) - - // Active data subscription id is sent, but no subscription change has been posted yet - getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() - .onActiveDataSubscriptionIdChanged(SUB_2_ID) - - // Subscriptions list is empty - assertThat(latestSubscriptions).isEmpty() - // getRepoForSubId does not throw - assertThat(latestActiveRepo).isNotNull() - } - - @Test - fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() = - testScope.runTest { - val activeRepo by collectLastValue(underTest.activeMobileDataRepository) - collectLastValue(underTest.subscriptions) - - // GIVEN active repo is updated before the subscription list updates - getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() - .onActiveDataSubscriptionIdChanged(SUB_2_ID) - - assertThat(activeRepo).isNotNull() - - // GIVEN the subscription list is then updated which includes the active data sub id - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_2)) - getSubscriptionCallback().onSubscriptionsChanged() - - // WHEN requesting a connection repository for the subscription - val newRepo = underTest.getRepoForSubId(SUB_2_ID) - - // THEN the newly request repo has been cached and reused - assertThat(activeRepo).isSameInstanceAs(newRepo) + activeRepo.invoke() // does not throw } @Test @@ -501,6 +732,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(subscriptionManager.completeActiveSubscriptionInfoList) .thenReturn(listOf(SUB_1)) getSubscriptionCallback().onSubscriptionsChanged() + runCurrent() val repo1 = underTest.getRepoForSubId(SUB_1_ID) val repo2 = underTest.getRepoForSubId(SUB_1_ID) @@ -525,80 +757,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(repo1).isSameInstanceAs(repo2) } - @Test - fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) - setWifiState(isCarrierMerged = true) - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_CM)) - getSubscriptionCallback().onSubscriptionsChanged() - - val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) - val mobileRepo = underTest.getRepoForSubId(SUB_1_ID) - assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() - assertThat(mobileRepo.getIsCarrierMerged()).isFalse() - } - - @Test - fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) - setWifiState(isCarrierMerged = true) - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_CM)) - getSubscriptionCallback().onSubscriptionsChanged() - - val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) - var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) - assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() - assertThat(mobileRepo.getIsCarrierMerged()).isFalse() - - // WHEN the wifi network updates to be not carrier merged - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) - setWifiState(isCarrierMerged = false) - runCurrent() - - // THEN the repos update - val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) - mobileRepo = underTest.getRepoForSubId(SUB_1_ID) - assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse() - assertThat(mobileRepo.getIsCarrierMerged()).isFalse() - } - - @Test - fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) - setWifiState(isCarrierMerged = false) - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_CM)) - getSubscriptionCallback().onSubscriptionsChanged() - runCurrent() - - val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) - var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) - assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse() - assertThat(mobileRepo.getIsCarrierMerged()).isFalse() - - // WHEN the wifi network updates to be carrier merged - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) - setWifiState(isCarrierMerged = true) - runCurrent() - - // THEN the repos update - val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) - mobileRepo = underTest.getRepoForSubId(SUB_1_ID) - assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() - assertThat(mobileRepo.getIsCarrierMerged()).isFalse() - } - @SuppressLint("UnspecifiedRegisterReceiverFlag") @Test fun testDeviceEmergencyCallState_eagerlyChecksState() = @@ -674,139 +832,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { } @Test - @Ignore("b/333912012") - fun testConnectionCache_clearsInvalidSubscriptions() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_2)) - getSubscriptionCallback().onSubscriptionsChanged() - - // Get repos to trigger caching - val repo1 = underTest.getRepoForSubId(SUB_1_ID) - val repo2 = underTest.getRepoForSubId(SUB_2_ID) - - assertThat(underTest.getSubIdRepoCache()) - .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) - - // SUB_2 disappears - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1)) - getSubscriptionCallback().onSubscriptionsChanged() - - assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) - } - - @Test - @Ignore("b/333912012") - fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) - setWifiState(isCarrierMerged = true) - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_2, SUB_CM)) - getSubscriptionCallback().onSubscriptionsChanged() - - // Get repos to trigger caching - val repo1 = underTest.getRepoForSubId(SUB_1_ID) - val repo2 = underTest.getRepoForSubId(SUB_2_ID) - val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID) - - assertThat(underTest.getSubIdRepoCache()) - .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged) - - // SUB_2 and SUB_CM disappear - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1)) - getSubscriptionCallback().onSubscriptionsChanged() - - assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) - } - - /** Regression test for b/261706421 */ - @Test - @Ignore("b/333912012") - fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() = - testScope.runTest { - collectLastValue(underTest.subscriptions) - - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1, SUB_2)) - getSubscriptionCallback().onSubscriptionsChanged() - - // Get repos to trigger caching - val repo1 = underTest.getRepoForSubId(SUB_1_ID) - val repo2 = underTest.getRepoForSubId(SUB_2_ID) - - assertThat(underTest.getSubIdRepoCache()) - .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) - - // All subscriptions disappear - whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) - getSubscriptionCallback().onSubscriptionsChanged() - - assertThat(underTest.getSubIdRepoCache()).isEmpty() - } - - @Test - fun testConnectionsCache_keepsReposCached() = - testScope.runTest { - // Collect subscriptions to start the job - collectLastValue(underTest.subscriptions) - - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1)) - getSubscriptionCallback().onSubscriptionsChanged() - - val repo1_1 = underTest.getRepoForSubId(SUB_1_ID) - - // All subscriptions disappear - whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) - getSubscriptionCallback().onSubscriptionsChanged() - - // Sub1 comes back - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1)) - getSubscriptionCallback().onSubscriptionsChanged() - - val repo1_2 = underTest.getRepoForSubId(SUB_1_ID) - - assertThat(repo1_1).isSameInstanceAs(repo1_2) - } - - @Test - fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() = - testScope.runTest { - // Collect subscriptions to start the job - collectLastValue(underTest.subscriptions) - - whenever(subscriptionManager.completeActiveSubscriptionInfoList) - .thenReturn(listOf(SUB_1)) - getSubscriptionCallback().onSubscriptionsChanged() - - // Client grabs a reference to a repository, but doesn't keep it around - underTest.getRepoForSubId(SUB_1_ID) - - // All subscriptions disappear - whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) - getSubscriptionCallback().onSubscriptionsChanged() - - val repo1 = underTest.getRepoForSubId(SUB_1_ID) - - assertThat(repo1).isNotNull() - } - - @Test - fun testConnectionRepository_invalidSubId_doesNotThrow() = - testScope.runTest { - underTest.getRepoForSubId(SUB_1_ID) - // No exception - } - - @Test fun connectionRepository_logBufferContainsSubIdInItsName() = testScope.runTest { collectLastValue(underTest.subscriptions) @@ -814,6 +839,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(subscriptionManager.completeActiveSubscriptionInfoList) .thenReturn(listOf(SUB_1, SUB_2)) getSubscriptionCallback().onSubscriptionsChanged() + runCurrent() // Get repos to trigger creation underTest.getRepoForSubId(SUB_1_ID) @@ -848,15 +874,27 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { fun defaultDataSubId_fetchesInitialValueOnStart() = testScope.runTest { subscriptionManagerProxy.defaultDataSubId = 2 + underTest = recreateRepo() + val latest by collectLastValue(underTest.defaultDataSubId) assertThat(latest).isEqualTo(2) } + private fun setDefaultDataSubId(subId: Int) { + subscriptionManagerProxy.defaultDataSubId = subId + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED).apply { + putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId) + }, + ) + } + @Test fun defaultDataSubId_filtersOutInvalidSubIds() = testScope.runTest { - subscriptionManagerProxy.defaultDataSubId = INVALID_SUBSCRIPTION_ID + setDefaultDataSubId(INVALID_SUBSCRIPTION_ID) val latest by collectLastValue(underTest.defaultDataSubId) assertThat(latest).isNull() @@ -865,15 +903,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { @Test fun defaultDataSubId_filtersOutInvalidSubIds_fromValidToInvalid() = testScope.runTest { - subscriptionManagerProxy.defaultDataSubId = 2 + setDefaultDataSubId(2) val latest by collectLastValue(underTest.defaultDataSubId) assertThat(latest).isEqualTo(2) - val intent = - Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) - .putExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID) - fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) + setDefaultDataSubId(INVALID_SUBSCRIPTION_ID) assertThat(latest).isNull() } @@ -881,7 +916,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { @Test fun defaultDataSubId_fetchesCurrentOnRestart() = testScope.runTest { - subscriptionManagerProxy.defaultDataSubId = 2 + setDefaultDataSubId(2) var latest: Int? = null var job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this) runCurrent() @@ -894,7 +929,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { latest = null - subscriptionManagerProxy.defaultDataSubId = 1 + setDefaultDataSubId(1) job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this) runCurrent() @@ -1281,26 +1316,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { // The initial value will be fetched when the repo is created, so we need to override // the resources and then re-create the repo. - underTest = - MobileConnectionsRepositoryImpl( - connectivityRepository, - subscriptionManager, - subscriptionManagerProxy, - telephonyManager, - logger, - summaryLogger, - mobileMappings, - fakeBroadcastDispatcher, - context, - testDispatcher, - testScope.backgroundScope, - testDispatcher, - airplaneModeRepository, - wifiRepository, - fullConnectionFactory, - updateMonitor, - mock(), - ) + underTest = recreateRepo() val latest by collectLastValue(underTest.defaultDataSubRatConfig) @@ -1428,6 +1444,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(underTest.getIsAnySimSecure()).isFalse() whenever(updateMonitor.isSimPinSecure).thenReturn(true) + org.mockito.kotlin + .argumentCaptor<KeyguardUpdateMonitorCallback>() + .apply { verify(updateMonitor, atLeast(0)).registerCallback(capture()) } + .allValues + .forEach { it.onSimStateChanged(0, 0, 0) } + runCurrent() assertThat(underTest.getIsAnySimSecure()).isTrue() } @@ -1447,19 +1469,26 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { testScope.runTest { whenever(telephonyManager.emergencyCallbackMode).thenReturn(true) + getTelephonyCallbacksForType<EmergencyCallbackModeListener>().forEach { + it.onCallbackModeStarted( + TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS, + Duration.ZERO, + 0, + ) + } runCurrent() assertThat(underTest.isInEcmMode()).isTrue() } - private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback { + protected fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback { runCurrent() val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>() verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture()) return callbackCaptor.value!! } - private fun setWifiState(isCarrierMerged: Boolean) { + protected fun setWifiState(isCarrierMerged: Boolean) { if (isCarrierMerged) { val mergedEntry = mock<MergedCarrierEntry>().apply { @@ -1481,7 +1510,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { wifiPickerTrackerCallback.value.onWifiEntriesChanged() } - private fun TestScope.getSubscriptionCallback(): + protected fun TestScope.getSubscriptionCallback(): SubscriptionManager.OnSubscriptionsChangedListener { runCurrent() val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>() @@ -1490,25 +1519,39 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { return callbackCaptor.value!! } - private fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> { + protected fun TestScope.getSubscriptionCallbacks(): + List<SubscriptionManager.OnSubscriptionsChangedListener> { + runCurrent() + val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>() + verify(subscriptionManager, atLeast(0)) + .addOnSubscriptionsChangedListener(any(), callbackCaptor.capture()) + return callbackCaptor.allValues + } + + fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> { runCurrent() val callbackCaptor = argumentCaptor<TelephonyCallback>() - verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture()) + verify(telephonyManager, atLeast(0)) + .registerTelephonyCallback(any(), callbackCaptor.capture()) return callbackCaptor.allValues } - private inline fun <reified T> TestScope.getTelephonyCallbackForType(): T { - val cbs = this.getTelephonyCallbacks().filterIsInstance<T>() + inline fun <reified T> TestScope.getTelephonyCallbackForType(): T { + val cbs = getTelephonyCallbacksForType<T>() assertThat(cbs.size).isEqualTo(1) return cbs[0] } + inline fun <reified T> TestScope.getTelephonyCallbacksForType(): List<T> { + return getTelephonyCallbacks().filterIsInstance<T>() + } + companion object { // Subscription 1 - private const val SUB_1_ID = 1 + const val SUB_1_ID = 1 private const val SUB_1_NAME = "Carrier $SUB_1_ID" private val GROUP_1 = ParcelUuid(UUID.randomUUID()) - private val SUB_1 = + val SUB_1 = mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) whenever(it.groupUuid).thenReturn(GROUP_1) @@ -1524,10 +1567,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { ) // Subscription 2 - private const val SUB_2_ID = 2 + const val SUB_2_ID = 2 private const val SUB_2_NAME = "Carrier $SUB_2_ID" private val GROUP_2 = ParcelUuid(UUID.randomUUID()) - private val SUB_2 = + val SUB_2 = mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) whenever(it.groupUuid).thenReturn(GROUP_2) @@ -1552,6 +1595,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED) whenever(it.groupUuid).thenReturn(GROUP_ID_3_4) whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) + whenever(it.carrierName).thenReturn("") } // Subscription 4 @@ -1561,17 +1605,18 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED) whenever(it.groupUuid).thenReturn(GROUP_ID_3_4) whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) + whenever(it.carrierName).thenReturn("") } // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private const val NET_ID = 123 - private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) } + val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) } // Carrier merged subscription - private const val SUB_CM_ID = 5 + const val SUB_CM_ID = 5 private const val SUB_CM_NAME = "Carrier $SUB_CM_ID" - private val SUB_CM = + val SUB_CM = mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) whenever(it.carrierName).thenReturn(SUB_CM_NAME) @@ -1590,7 +1635,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(this.isCarrierMerged).thenReturn(true) whenever(this.subscriptionId).thenReturn(SUB_CM_ID) } - private val WIFI_NETWORK_CAPS_CM = + val WIFI_NETWORK_CAPS_CM = mock<NetworkCapabilities>().also { whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) whenever(it.transportInfo).thenReturn(WIFI_INFO_CM) @@ -1602,7 +1647,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { whenever(this.isPrimary).thenReturn(true) whenever(this.isCarrierMerged).thenReturn(false) } - private val WIFI_NETWORK_CAPS_ACTIVE = + val WIFI_NETWORK_CAPS_ACTIVE = mock<NetworkCapabilities>().also { whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index 3247a1ab6eb0..8ff8fe6cc3d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -44,6 +44,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.view.RotationPolicy; +import com.android.settingslib.devicestate.AndroidSecureSettings; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; @@ -117,7 +118,8 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); mContentResolver = mContext.getContentResolver(); - mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext); + mSettingsManager = new DeviceStateRotationLockSettingsManager(mContext, + new AndroidSecureSettings(mContentResolver)); mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController( mFakeRotationPolicy, diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt index 54df9e99baa5..bb6ba46f1a0b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt @@ -25,13 +25,12 @@ import android.view.WindowManager import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.InstanceId import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager -import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener import com.android.systemui.util.concurrency.DelayableExecutor @@ -78,7 +77,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager @Mock - private lateinit var windowManager: ViewCaptureAwareWindowManager + private lateinit var windowManager: WindowManager @Mock private lateinit var powerManager: PowerManager @@ -1143,7 +1142,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() { inner class TestController( context: Context, logger: TemporaryViewLogger<ViewInfo>, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, @Main mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, @@ -1155,7 +1154,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() { ) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger<ViewInfo>>( context, logger, - viewCaptureAwareWindowManager, + windowManager, mainExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt index 4260b6558950..664f2df62782 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt @@ -30,8 +30,6 @@ import android.widget.TextView import androidx.core.animation.doOnCancel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.internal.logging.InstanceId import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase @@ -86,7 +84,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() { @Mock private lateinit var viewUtil: ViewUtil @Mock private lateinit var vibratorHelper: VibratorHelper @Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler - @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture> private lateinit var chipbarAnimator: TestChipbarAnimator private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder private lateinit var fakeWakeLock: WakeLockFake @@ -115,8 +112,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() { ChipbarCoordinator( context, logger, - ViewCaptureAwareWindowManager(windowManager, lazyViewCapture, - isViewCaptureEnabled = false), + windowManager, fakeExecutor, accessibilityManager, configurationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt new file mode 100644 index 000000000000..7b52237f0a01 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt @@ -0,0 +1,53 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils + +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.WindowManager +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.utils.windowmanager.WindowManagerProviderImpl +import com.google.common.truth.Truth.assertThat +import org.junit.runner.RunWith +import org.junit.Test + +@RunWith(AndroidJUnit4::class) +@SmallTest +class WindowManagerProviderImplTest : SysuiTestCase() { + + private val windowManagerProvider = WindowManagerProviderImpl() + private val windowManagerFromSystemService = mContext.getSystemService(WindowManager::class.java) + + @Test + @EnableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING) + fun viewCaptureTracingEnabled_verifyWMInstanceDoesNotMatchContextOne() { + val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext) + assertThat(windowManagerFromProvider).isNotEqualTo(windowManagerFromSystemService) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING) + fun viewCaptureTracingDisabled_verifyWMInstanceMatchesContextOne() { + mContext.addMockSystemService(WindowManager::class.java, windowManagerFromSystemService) + + val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext) + assertThat(windowManagerFromProvider).isEqualTo(windowManagerFromSystemService) + } +}
\ No newline at end of file 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 0d7ce5353cd4..f89571f2db2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -101,8 +101,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; -import com.android.app.viewcapture.ViewCapture; -import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.UiEventLogger; import com.android.internal.protolog.ProtoLog; @@ -358,8 +356,6 @@ public class BubblesTest extends SysuiTestCase { @Mock private Display mDefaultDisplay; @Mock - private Lazy<ViewCapture> mLazyViewCapture; - @Mock private SyncTransactionQueue mSyncQueue; private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); @@ -429,8 +425,7 @@ public class BubblesTest extends SysuiTestCase { mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl( mContext, new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView), - new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture, - /* isViewCaptureEnabled= */ false), + mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt index 8a597a61ee78..47b1bf52d8e2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt @@ -16,18 +16,18 @@ package com.android.systemui.communal.posturing.data.repository -import com.android.systemui.communal.posturing.shared.model.PosturedState -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import com.android.systemui.communal.posturing.data.model.PositionState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow class FakePosturingRepository : PosturingRepository { - private val _postured = MutableStateFlow<PosturedState>(PosturedState.Unknown) + private val _postured = MutableSharedFlow<PositionState>() - override val posturedState: StateFlow<PosturedState> = _postured.asStateFlow() + override val positionState: Flow<PositionState> = _postured.asSharedFlow() - fun setPosturedState(state: PosturedState) { - _postured.value = state + suspend fun emitPositionState(state: PositionState) { + _postured.emit(state) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt index 53c9c6440c69..792346ebce59 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt @@ -18,6 +18,27 @@ package com.android.systemui.communal.posturing.domain.interactor import com.android.systemui.communal.posturing.data.repository.posturingRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.advanceTimeBy +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.log.logcatLogBuffer +import com.android.systemui.util.sensors.asyncSensorManager +import com.android.systemui.util.time.systemClock val Kosmos.posturingInteractor by - Kosmos.Fixture<PosturingInteractor> { PosturingInteractor(repository = posturingRepository) } + Kosmos.Fixture<PosturingInteractor> { + PosturingInteractor( + repository = posturingRepository, + asyncSensorManager = asyncSensorManager, + applicationScope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + logBuffer = logcatLogBuffer("PosturingInteractor"), + clock = systemClock, + ) + } + +fun Kosmos.advanceTimeBySlidingWindowAndRun() { + advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION) + runCurrent() +} 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 044332981bf8..ae28022e096e 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 @@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.Kosmos.Fixture import kotlin.coroutines.CoroutineContext +import kotlin.time.Duration import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -32,6 +33,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import org.mockito.kotlin.verify @@ -72,6 +74,8 @@ fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) = let { kosmos -> fun Kosmos.runCurrent() = testScope.runCurrent() +fun Kosmos.advanceTimeBy(duration: Duration) = testScope.advanceTimeBy(duration) + fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow) fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt index 2f3d3c3e0489..ba1c598a79d7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.controls.ui.viewmodel import android.content.applicationContext +import com.android.internal.logging.InstanceId import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher @@ -31,5 +32,9 @@ val Kosmos.mediaControlViewModel by backgroundExecutor = fakeExecutor, interactor = mediaControlInteractor, logger = mediaUiEventLogger, + instanceId = InstanceId.fakeInstanceId(-1), + onAdded = {}, + onRemoved = {}, + onUpdated = {}, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt index aae32cfaafa6..f83fcb32aafe 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.data.repository -import android.view.WindowManager -import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayWindowPropertiesRepository import com.android.systemui.kosmos.Kosmos @@ -35,14 +33,6 @@ val Kosmos.privacyDotWindowControllerStoreImpl by windowControllerFactory = { _, _, _, _ -> mock() }, displayWindowPropertiesRepository = displayWindowPropertiesRepository, privacyDotViewControllerStore = privacyDotViewControllerStore, - viewCaptureAwareWindowManagerFactory = - object : ViewCaptureAwareWindowManager.Factory { - override fun create( - windowManager: WindowManager - ): ViewCaptureAwareWindowManager { - return mock() - } - }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt index c73838708a7a..da4027e46783 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.events import android.content.testableContext +import android.view.fakeWindowManager import android.view.layoutInflater -import com.android.app.viewcapture.realCaptureAwareWindowManager import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.decor.privacyDotDecorProviderFactory import com.android.systemui.kosmos.Kosmos @@ -28,7 +28,7 @@ var Kosmos.privacyDotWindowController by PrivacyDotWindowController( testableContext.displayId, privacyDotViewController, - realCaptureAwareWindowManager, + fakeWindowManager, layoutInflater, fakeExecutor, privacyDotDecorProviderFactory, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt index 3a19547f0713..f20fb3bf1779 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt @@ -17,14 +17,14 @@ package com.android.systemui.statusbar.window import android.content.Context -import com.android.app.viewcapture.ViewCaptureAwareWindowManager +import android.view.WindowManager import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory { override fun create( context: Context, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + windowManager: WindowManager, statusBarConfigurationController: StatusBarConfigurationController, contentInsetsProvider: StatusBarContentInsetsProvider, ) = FakeStatusBarWindowController() 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 f595aef41e2d..b0214769362f 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 @@ -17,8 +17,8 @@ package com.android.systemui.statusbar.window import android.content.testableContext +import android.view.fakeWindowManager import android.view.windowManagerService -import com.android.app.viewcapture.realCaptureAwareWindowManager import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.fragments.fragmentService import com.android.systemui.kosmos.Kosmos @@ -33,7 +33,7 @@ val Kosmos.statusBarWindowControllerImpl by StatusBarWindowControllerImpl( testableContext, statusBarWindowViewInflater, - realCaptureAwareWindowManager, + fakeWindowManager, statusBarConfigurationController, windowManagerService, statusBarContentInsetsProvider, 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 index 4941ceb7991d..0f9310376b2a 100644 --- 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 @@ -16,9 +16,6 @@ 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 @@ -33,14 +30,6 @@ val Kosmos.multiDisplayStatusBarWindowControllerStore by 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/utils/windowmanager/FakeWindowManagerProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt new file mode 100644 index 000000000000..5c8eae3183c7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt @@ -0,0 +1,28 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils.windowmanager + +import android.content.Context +import android.view.WindowManager + +/** Fake implementation of [WindowManagerProvider], to be used in tests only. */ +class FakeWindowManagerProvider(private val windowManager: WindowManager) : WindowManagerProvider { + + override fun getWindowManager(context: Context): WindowManager { + return windowManager + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt index 021c7bbb44cd..fc7fc0f4e5b4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt @@ -14,23 +14,13 @@ * limitations under the License. */ -package com.android.app.viewcapture +package com.android.systemui.volume.dialog.domain.interactor -import android.view.fakeWindowManager import com.android.systemui.kosmos.Kosmos -import org.mockito.kotlin.mock +import com.android.systemui.volume.dialog.shared.model.CsdWarningConfigModel -val Kosmos.mockViewCaptureAwareWindowManager by - Kosmos.Fixture { mock<ViewCaptureAwareWindowManager>() } +val Kosmos.volumeDialogCsdWarningInteractor: VolumeDialogCsdWarningInteractor by + Kosmos.Fixture { VolumeDialogCsdWarningInteractor(volumeDialogStateInteractor) } -val Kosmos.realCaptureAwareWindowManager by - Kosmos.Fixture { - ViewCaptureAwareWindowManager( - fakeWindowManager, - lazyViewCapture = lazy { mock<ViewCapture>() }, - isViewCaptureEnabled = false, - ) - } - -var Kosmos.viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager by - Kosmos.Fixture { mockViewCaptureAwareWindowManager } +val Kosmos.csdWarningConfigModel: CsdWarningConfigModel by + Kosmos.Fixture { CsdWarningConfigModel(emptyList()) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt new file mode 100644 index 000000000000..5a58599eaade --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt @@ -0,0 +1,41 @@ +/* + * 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, 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 com.android.internal.logging.uiEventLogger +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.backgroundScope +import com.android.systemui.volume.dialog.domain.interactor.csdWarningConfigModel +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogCsdWarningInteractor +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogSafetyWarningInteractor +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor +import com.android.systemui.volume.dialog.shared.volumeDialogLogger +import com.android.systemui.volume.dialog.volumeDialog + +val Kosmos.volumeDialogPluginViewModel: VolumeDialogPluginViewModel by + Kosmos.Fixture { + VolumeDialogPluginViewModel( + backgroundScope, + volumeDialogVisibilityInteractor, + volumeDialogSafetyWarningInteractor, + volumeDialogCsdWarningInteractor, + { volumeDialog }, + volumeDialogLogger, + csdWarningConfigModel, + uiEventLogger, + ) + } diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp index 1efb11b436ff..8b63c07b270f 100644 --- a/packages/SystemUI/utils/Android.bp +++ b/packages/SystemUI/utils/Android.bp @@ -26,6 +26,8 @@ java_library { "src/**/*.kt", ], static_libs: [ + "//frameworks/libs/systemui:view_capture", + "com_android_systemui_flags_lib", "kotlin-stdlib", "kotlinx_coroutines", ], diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt new file mode 100644 index 000000000000..4e6eacbc8808 --- /dev/null +++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt @@ -0,0 +1,56 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils.windowmanager + +import android.content.Context +import android.view.WindowManager + +/** + * Provider for [WindowManager] in SystemUI. + * + * Use this class over [WindowManagerUtils] in cases where + * a [WindowManager] is needed for a context created inside the class. [WindowManagerUtils] should + * only be used in a class where the [WindowManager] is needed for a custom context inside the + * class, and the class is not part of the dagger graph. Example usage: + * ```kotlin + * class Sample { + * private final WindowManager mWindowManager; + * + * @Inject + * public Sample(WindowManagerProvider windowManagerProvider) { + * Context context = getCustomContext(); + * mWindowManager = windowManagerProvider.getWindowManager(context); + * } + * // use mWindowManager + * } + * + * class SampleTest { + * + * @Mock + * WindowManager mWindowManager; + * + * FakeWindowManagerProvider fakeProvider = new FakeWindowManagerProvider(mWindowManager); + * + * // define the behaviour of mWindowManager to get required WindowManager instance in tests. + * } + * ``` + */ +interface WindowManagerProvider { + + /** Method to return the required [WindowManager]. */ + fun getWindowManager(context: Context): WindowManager +} diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt new file mode 100644 index 000000000000..5e965ed47403 --- /dev/null +++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt @@ -0,0 +1,28 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils.windowmanager + +import android.content.Context +import android.view.WindowManager + +/** Implementation of [WindowManagerProvider]. */ +class WindowManagerProviderImpl : WindowManagerProvider { + + override fun getWindowManager(context: Context): WindowManager { + return WindowManagerUtils.getWindowManager(context) + } +} diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt new file mode 100644 index 000000000000..643e93422294 --- /dev/null +++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt @@ -0,0 +1,49 @@ +/* + * 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, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils.windowmanager + +import android.content.Context +import android.view.WindowManager +import com.android.app.viewcapture.ViewCaptureAwareWindowManagerFactory +import com.android.systemui.Flags.enableViewCaptureTracing + +/** + * Provides [WindowManager] in SystemUI. Use [WindowManagerProvider] unless [WindowManager] instance + * needs to be created in a class that is not part of the dagger dependency graph. + */ +object WindowManagerUtils { + + /** Method to return the required [WindowManager]. */ + @JvmStatic + fun getWindowManager(context: Context): WindowManager { + return if (!enableViewCaptureTracing()) { + context.getSystemService(WindowManager::class.java) + } else { + /** + * We use this token to supply windowContextToken to [WindowManager] for + * [WindowContext]. + */ + val windowContextToken = context.windowContextToken + + ViewCaptureAwareWindowManagerFactory.getInstance( + context, + parent = null, + windowContextToken, + ) + } + } +} diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index f8bb526d0a86..760999f5e129 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -60,7 +60,7 @@ class ClassWidePolicyPropagatingFilter( } return p.withReason(policy.reason) - .wrapReason("class-wide in $className") + .wrapReason("class-wide in $className", policy.statsLabelOverride) } // If the class's policy is remove, then remove it. if (policy.policy == FilterPolicy.Remove) { diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt index 7358a0bfb3e6..e082bbb0a119 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt @@ -41,7 +41,7 @@ enum class StatsLabel(val statValue: Int, val label: String) { data class FilterPolicyWithReason ( val policy: FilterPolicy, val reason: String = "", - private val statsLabelOverride: StatsLabel? = null + val statsLabelOverride: StatsLabel? = null ) { /** * Return a new [FilterPolicy] with an updated reason, while keeping the original reason @@ -51,7 +51,7 @@ data class FilterPolicyWithReason ( return FilterPolicyWithReason( policy, "$reason [inner-reason: ${this.reason}]", - statsLabelOverride = statsLabelOverride, + statsLabelOverride = statsLabelOverride ?: this.statsLabelOverride, ) } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index 97fc35302528..cdcea4c15820 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -528,7 +528,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class AIDL)" + "$FILTER_REASON (special-class AIDL)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) aidlPolicy = p @@ -541,7 +542,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class feature flags)" + "$FILTER_REASON (special-class feature flags)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) featureFlagsPolicy = p @@ -554,7 +556,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class sysprops)" + "$FILTER_REASON (special-class sysprops)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) syspropsPolicy = p @@ -567,7 +570,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class R file)" + "$FILTER_REASON (special-class R file)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) rFilePolicy = p diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 47aa8f5736bf..aae8879e9199 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1942,14 +1942,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public void notifyGesture(AccessibilityGestureEvent gestureEvent) { - if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { - // We will use this event async, so copy it because it contains MotionEvents. - mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, - gestureEvent.copyForAsync()).sendToTarget(); - } else { - mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, - gestureEvent).sendToTarget(); - } + // We will use this event async, so copy it because it contains MotionEvents. + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, + gestureEvent.copyForAsync()).sendToTarget(); } public void notifySystemActionsChangedLocked() { @@ -2426,9 +2421,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ case MSG_ON_GESTURE: { if (message.obj instanceof AccessibilityGestureEvent gesture) { notifyGestureInternal(gesture); - if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { - gesture.recycle(); - } + gesture.recycle(); } } break; case MSG_CLEAR_ACCESSIBILITY_CACHE: { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java index 3668eefe293d..62b6b85afa58 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java @@ -336,13 +336,8 @@ public abstract class GestureMatcher { // Recycle the old event first if necessary, to handle duplicate calls to post. recycleEvent(); mTargetState = state; - if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { - mEvent = event.copy(); - mRawEvent = rawEvent.copy(); - } else { - mEvent = event; - mRawEvent = rawEvent; - } + mEvent = event.copy(); + mRawEvent = rawEvent.copy(); mPolicyFlags = policyFlags; mHandler.postDelayed(this, delay); if (DEBUG) { @@ -379,15 +374,13 @@ public abstract class GestureMatcher { } private void recycleEvent() { - if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { - if (mEvent == null || mRawEvent == null) { - return; - } - mEvent.recycle(); - mRawEvent.recycle(); - mEvent = null; - mRawEvent = null; + if (mEvent == null || mRawEvent == null) { + return; } + mEvent.recycle(); + mRawEvent.recycle(); + mEvent = null; + mRawEvent = null; } } diff --git a/services/core/Android.bp b/services/core/Android.bp index decac40d20f8..cf85dd957b3f 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -258,7 +258,6 @@ java_library_static { "dreams_flags_lib", "aconfig_new_storage_flags_lib", "powerstats_flags_lib", - "locksettings_flags_lib", "MmdProperties", "mmd_flags_lib", "profiling_flags_lib", diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5395d2a914ec..c15915ba39a4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -790,7 +790,7 @@ public final class ActiveServices { "SHORT_FGS_TIMEOUT"); this.mServiceFGAnrTimer = new ServiceAnrTimer(service, ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, - "SERVICE_FOREGROUND_TIMEOUT"); + "SERVICE_FOREGROUND_TIMEOUT", new AnrTimer.Args().extend(true)); } void systemServicesReady() { @@ -7702,6 +7702,11 @@ public final class ActiveServices { super(Objects.requireNonNull(am).mHandler, msg, label); } + ServiceAnrTimer(ActivityManagerService am, int msg, String label, + @NonNull AnrTimer.Args args) { + super(Objects.requireNonNull(am).mHandler, msg, label, args); + } + @Override public int getPid(@NonNull ServiceRecord service) { return (service.app != null) ? service.app.getPid() : 0; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index a8bb5231d8c0..b1acfe830eed 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4190,7 +4190,13 @@ public class AudioService extends IAudioService.Stub Log.d(TAG, "adjustStreamVolume postSetHearingAidVolumeIndex index=" + newIndex + " stream=" + streamType); } - mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType); + int haIndex; + final VolumeStreamState vss = getVssForStreamOrDefault(streamType); + synchronized (mVolumeStateLock) { + haIndex = (int) (vss.getMinIndex() + (newIndex - vss.getMinIndex()) + / vss.getIndexStepFactor()); + } + mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType); } } @@ -4460,18 +4466,12 @@ public class AudioService extends IAudioService.Stub private final AudioVolumeChangeHandler mAudioVolumeChangeHandler; /** @see AudioManager#registerVolumeGroupCallback(executor, callback) */ - @android.annotation.EnforcePermission( - android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) { - super.registerAudioVolumeCallback_enforcePermission(); mAudioVolumeChangeHandler.registerListener(callback); } /** @see AudioManager#unregisterVolumeGroupCallback(callback) */ - @android.annotation.EnforcePermission( - android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) { - super.unregisterAudioVolumeCallback_enforcePermission(); mAudioVolumeChangeHandler.unregisterListener(callback); } @@ -5149,7 +5149,13 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.postSetLeAudioVolumeIndex(index * 10, getVssForStreamOrDefault(streamType).getMaxIndex(), streamType); } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { - mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + int haIndex = index * 10; + final VolumeStreamState vss = getVssForStreamOrDefault(streamType); + synchronized (mVolumeStateLock) { + haIndex = (int) (vss.getMinIndex() + + (haIndex - vss.getMinIndex()) / vss.getIndexStepFactor()); + } + mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType); } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) { mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index); } else { @@ -5280,7 +5286,13 @@ public class AudioService extends IAudioService.Stub && streamType == getBluetoothContextualVolumeStream()) { Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index + " stream=" + streamType); - mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType); + int haIndex; + final VolumeStreamState vss = getVssForStreamOrDefault(streamType); + synchronized (mVolumeStateLock) { + haIndex = (int) (vss.getMinIndex() + + (index - vss.getMinIndex()) / vss.getIndexStepFactor()); + } + mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType); } synchronized (mHdmiClientLock) { diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 60b7fca99e7b..228e6f1c4ddb 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -130,6 +130,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter { private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE = "fixed_content_mode"; + /** + * When this flag is set, disables support for moving and resizing the overlay window. + * As the window is made non-touchable, this also makes it possible to directly interact with + * the content underneath. + */ + private static final String OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION = + "disable_window_interaction"; + // Gravity flags to decide where the overlay should be shown. private static final String GRAVITY_TOP_LEFT = "gravity_top_left"; private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right"; @@ -571,9 +579,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { @Override public void run() { OverlayMode mode = mModes.get(mActiveMode); - OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), - mName, mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, - mFlags.mSecure, OverlayDisplayHandle.this); + OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), mName, + mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, mFlags.mSecure, + mFlags.mDisableWindowInteraction, OverlayDisplayHandle.this); window.show(); synchronized (getSyncRoot()) { @@ -655,6 +663,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */ final boolean mFixedContentMode; + /** See {@link #OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION}. */ + final boolean mDisableWindowInteraction; + final int mGravity; OverlayFlags( @@ -662,11 +673,13 @@ final class OverlayDisplayAdapter extends DisplayAdapter { boolean ownContentOnly, boolean shouldShowSystemDecorations, boolean fixedContentMode, + boolean disableWindowInteraction, int gravity) { mSecure = secure; mOwnContentOnly = ownContentOnly; mShouldShowSystemDecorations = shouldShowSystemDecorations; mFixedContentMode = fixedContentMode; + mDisableWindowInteraction = disableWindowInteraction; mGravity = gravity; } @@ -677,6 +690,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { false /* ownContentOnly */, false /* shouldShowSystemDecorations */, false /* fixedContentMode */, + false /* disableWindowInteraction */, Gravity.NO_GRAVITY); } @@ -684,6 +698,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { boolean ownContentOnly = false; boolean shouldShowSystemDecorations = false; boolean fixedContentMode = false; + boolean disableWindowInteraction = false; int gravity = Gravity.NO_GRAVITY; for (String flag: flagString.split(FLAG_SPLITTER)) { if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) { @@ -694,12 +709,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter { shouldShowSystemDecorations = true; } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) { fixedContentMode = true; + } else if (OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION.equals(flag)) { + disableWindowInteraction = true; } else { gravity = parseOverlayGravity(flag); } } return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, - fixedContentMode, gravity); + fixedContentMode, disableWindowInteraction, gravity); } @Override @@ -709,6 +726,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { .append(", ownContentOnly=").append(mOwnContentOnly) .append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations) .append(", fixedContentMode=").append(mFixedContentMode) + .append(", disableWindowInteraction=").append(mDisableWindowInteraction) .append(", gravity").append(Gravity.toString(mGravity)) .append("}") .toString(); diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index 3fd58e8641c3..523bbfa7d69a 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -69,6 +69,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { private int mDensityDpi; private final int mGravity; private final boolean mSecure; + private final boolean mDisableWindowInteraction; private final Listener mListener; private String mTitle; @@ -96,15 +97,15 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { private float mLiveTranslationY; private float mLiveScale = 1.0f; - public OverlayDisplayWindow(Context context, String name, - int width, int height, int densityDpi, int gravity, boolean secure, - Listener listener) { + OverlayDisplayWindow(Context context, String name, int width, int height, int densityDpi, + int gravity, boolean secure, boolean disableWindowInteraction, Listener listener) { // Workaround device freeze (b/38372997) ThreadedRenderer.disableVsync(); mContext = context; mName = name; mGravity = gravity; mSecure = secure; + mDisableWindowInteraction = disableWindowInteraction; mListener = listener; mDisplayManager = (DisplayManager)context.getSystemService( @@ -226,8 +227,10 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { if (mSecure) { mWindowParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; } - if (DISABLE_MOVE_AND_RESIZE) { + if (DISABLE_MOVE_AND_RESIZE || mDisableWindowInteraction) { mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + mWindowParams.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; } mWindowParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 29e04e744759..6af55300d0b3 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -25,6 +25,7 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.hardware.input.Flags.enableCustomizableInputGestures; +import static com.android.hardware.input.Flags.fixSearchModifierFallbacks; import static com.android.hardware.input.Flags.keyEventActivityDetection; import static com.android.hardware.input.Flags.touchpadVisualizer; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; @@ -750,7 +751,8 @@ public class InputManagerService extends IInputManager.Stub * @return True if the lookup was successful, false otherwise. */ @Override // Binder call - public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) { + public boolean hasKeys(int deviceId, int sourceMask, @NonNull int[] keyCodes, + @NonNull boolean[] keyExists) { Objects.requireNonNull(keyCodes, "keyCodes must not be null"); Objects.requireNonNull(keyExists, "keyExists must not be null"); if (keyExists.length < keyCodes.length) { @@ -791,7 +793,7 @@ public class InputManagerService extends IInputManager.Stub * @deprecated Use {@link #transferTouchGesture(IBinder, IBinder)} */ @Deprecated - public boolean transferTouch(IBinder destChannelToken, int displayId) { + public boolean transferTouch(@NonNull IBinder destChannelToken, int displayId) { // TODO(b/162194035): Replace this with a SPY window Objects.requireNonNull(destChannelToken, "destChannelToken must not be null"); return mNative.transferTouch(destChannelToken, displayId); @@ -803,7 +805,7 @@ public class InputManagerService extends IInputManager.Stub * @param displayId Target display id. * @return The input channel. */ - public InputChannel monitorInput(String inputChannelName, int displayId) { + public InputChannel monitorInput(@NonNull String inputChannelName, int displayId) { Objects.requireNonNull(inputChannelName, "inputChannelName not be null"); if (displayId < Display.DEFAULT_DISPLAY) { @@ -835,7 +837,7 @@ public class InputManagerService extends IInputManager.Stub return outInputChannel; } - private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) { + private void removeSpyWindowGestureMonitor(@NonNull IBinder inputChannelToken) { final GestureMonitorSpyWindow monitor; synchronized (mInputMonitors) { monitor = mInputMonitors.remove(inputChannelToken); @@ -854,8 +856,8 @@ public class InputManagerService extends IInputManager.Stub * @return The input channel. */ @Override // Binder call - public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName, - int displayId) { + public InputMonitor monitorGestureInput(@NonNull IBinder monitorToken, + @NonNull String requestedName, int displayId) { if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, "monitorGestureInput()")) { throw new SecurityException("Requires MONITOR_INPUT permission"); @@ -902,7 +904,7 @@ public class InputManagerService extends IInputManager.Stub * Removes an input channel. * @param connectionToken The input channel to unregister. */ - public void removeInputChannel(IBinder connectionToken) { + public void removeInputChannel(@NonNull IBinder connectionToken) { Objects.requireNonNull(connectionToken, "connectionToken must not be null"); mNative.removeInputChannel(connectionToken); } @@ -977,12 +979,12 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public boolean injectInputEvent(InputEvent event, int mode) { + public boolean injectInputEvent(@NonNull InputEvent event, int mode) { return injectInputEventToTarget(event, mode, Process.INVALID_UID); } @Override // Binder call - public boolean injectInputEventToTarget(InputEvent event, int mode, int targetUid) { + public boolean injectInputEventToTarget(@NonNull InputEvent event, int mode, int targetUid) { if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS, "injectInputEvent()", true /*checkInstrumentationSource*/)) { throw new SecurityException( @@ -1032,7 +1034,7 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public VerifiedInputEvent verifyInputEvent(InputEvent event) { + public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) { Objects.requireNonNull(event, "event must not be null"); return mNative.verifyInputEvent(event); } @@ -1106,7 +1108,8 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) { + public void registerInputDevicesChangedListener( + @NonNull IInputDevicesChangedListener listener) { Objects.requireNonNull(listener, "listener must not be null"); synchronized (mInputDevicesLock) { @@ -1176,7 +1179,7 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call & native callback - public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, + public TouchCalibration getTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor, int surfaceRotation) { Objects.requireNonNull(inputDeviceDescriptor, "inputDeviceDescriptor must not be null"); @@ -1186,8 +1189,8 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation, - TouchCalibration calibration) { + public void setTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor, + int surfaceRotation, @NonNull TouchCalibration calibration) { if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION, "setTouchCalibrationForInputDevice()")) { throw new SecurityException("Requires SET_INPUT_CALIBRATION permission"); @@ -1225,7 +1228,7 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public void registerTabletModeChangedListener(ITabletModeChangedListener listener) { + public void registerTabletModeChangedListener(@NonNull ITabletModeChangedListener listener) { if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE, "registerTabletModeChangedListener()")) { throw new SecurityException("Requires TABLET_MODE_LISTENER permission"); @@ -1341,7 +1344,7 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) { + public void requestPointerCapture(@NonNull IBinder inputChannelToken, boolean enabled) { Objects.requireNonNull(inputChannelToken, "inputChannelToken must not be null"); mNative.requestPointerCapture(inputChannelToken, enabled); @@ -1664,7 +1667,8 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + public boolean registerVibratorStateListener(int deviceId, + @NonNull IVibratorStateListener listener) { Objects.requireNonNull(listener, "listener must not be null"); RemoteCallbackList<IVibratorStateListener> listeners; @@ -1717,8 +1721,8 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override - public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId, - IBinder inputToken) { + public boolean setPointerIcon(@NonNull PointerIcon icon, int displayId, int deviceId, + int pointerId, IBinder inputToken) { Objects.requireNonNull(icon); return mNative.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken); } @@ -1896,7 +1900,7 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public boolean registerSensorListener(IInputSensorEventListener listener) { + public boolean registerSensorListener(@NonNull IInputSensorEventListener listener) { if (DEBUG) { Slog.d(TAG, "registerSensorListener: listener=" + listener + " callingPid=" + Binder.getCallingPid()); @@ -1927,7 +1931,7 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call - public void unregisterSensorListener(IInputSensorEventListener listener) { + public void unregisterSensorListener(@NonNull IInputSensorEventListener listener) { if (DEBUG) { Slog.d(TAG, "unregisterSensorListener: listener=" + listener + " callingPid=" + Binder.getCallingPid()); @@ -2016,7 +2020,8 @@ public class InputManagerService extends IInputManager.Stub /** * Set specified light state with for a specific input device. */ - private void setLightStateInternal(int deviceId, Light light, LightState lightState) { + private void setLightStateInternal(int deviceId, @NonNull Light light, + @NonNull LightState lightState) { Objects.requireNonNull(light, "light does not exist"); if (DEBUG) { Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light @@ -2079,7 +2084,7 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void openLightSession(int deviceId, String opPkg, IBinder token) { + public void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { Objects.requireNonNull(token); synchronized (mLightLock) { Preconditions.checkState(mLightSessions.get(token) == null, "already registered"); @@ -2098,7 +2103,7 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void closeLightSession(int deviceId, IBinder token) { + public void closeLightSession(int deviceId, @NonNull IBinder token) { Objects.requireNonNull(token); synchronized (mLightLock) { LightSession lightSession = mLightSessions.get(token); @@ -2128,13 +2133,15 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener) { + public void registerBatteryListener(int deviceId, + @NonNull IInputDeviceBatteryListener listener) { Objects.requireNonNull(listener); mBatteryController.registerBatteryListener(deviceId, listener, Binder.getCallingPid()); } @Override - public void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener) { + public void unregisterBatteryListener(int deviceId, + @NonNull IInputDeviceBatteryListener listener) { Objects.requireNonNull(listener); mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid()); } @@ -2155,7 +2162,7 @@ public class InputManagerService extends IInputManager.Stub @EnforcePermission(Manifest.permission.MONITOR_INPUT) @Override - public void pilferPointers(IBinder inputChannelToken) { + public void pilferPointers(@NonNull IBinder inputChannelToken) { super.pilferPointers_enforcePermission(); Objects.requireNonNull(inputChannelToken); @@ -2164,7 +2171,7 @@ public class InputManagerService extends IInputManager.Stub @Override @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT) - public void registerKeyboardBacklightListener(IKeyboardBacklightListener listener) { + public void registerKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) { super.registerKeyboardBacklightListener_enforcePermission(); Objects.requireNonNull(listener); mKeyboardBacklightController.registerKeyboardBacklightListener(listener, @@ -2173,7 +2180,7 @@ public class InputManagerService extends IInputManager.Stub @Override @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT) - public void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener) { + public void unregisterKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) { super.unregisterKeyboardBacklightListener_enforcePermission(); Objects.requireNonNull(listener); mKeyboardBacklightController.unregisterKeyboardBacklightListener(listener, @@ -2653,6 +2660,8 @@ public class InputManagerService extends IInputManager.Stub @SuppressWarnings("unused") @VisibleForTesting long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) { + final long keyNotConsumedGoFallback = -2; + final long keyConsumed = -1; final long keyNotConsumed = 0; long value = keyNotConsumed; // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts @@ -2667,6 +2676,16 @@ public class InputManagerService extends IInputManager.Stub value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags); } + if (fixSearchModifierFallbacks() && value == keyNotConsumed && event.isMetaPressed()) { + // If the key has not been consumed and includes the meta key, do not send the event + // to the app and attempt to generate a fallback. + final KeyCharacterMap kcm = event.getKeyCharacterMap(); + final KeyCharacterMap.FallbackAction fallbackAction = + kcm.getFallbackAction(event.getKeyCode(), event.getMetaState()); + if (fallbackAction != null) { + return keyNotConsumedGoFallback; + } + } return value; } @@ -3310,9 +3329,10 @@ public class InputManagerService extends IInputManager.Stub * @param token the window token that's about to receive this event * @param event the key event that's being dispatched * @param policyFlags the policy flags - * @return negative value if the key should be skipped (not sent to the app). 0 if the key - * should proceed getting dispatched to the app. positive value to indicate the additional - * time delay, in nanoseconds, to wait before sending this key to the app. + * @return -1 if the key should be skipped (not sent to the app). -2 if the key should not + * be sent to the app, but it should still generate a fallback. + * 0 if the key should proceed getting dispatched to the app. positive value to indicate the + * additional time delay, in nanoseconds, to wait before sending this key to the app. */ long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); @@ -3429,7 +3449,7 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void sendInputEvent(InputEvent event, int policyFlags) { + public void sendInputEvent(@NonNull InputEvent event, int policyFlags) { if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS, "sendInputEvent()")) { throw new SecurityException( @@ -3451,9 +3471,9 @@ public class InputManagerService extends IInputManager.Stub * Interface for the system to handle request from InputMonitors. */ private final class InputMonitorHost extends IInputMonitorHost.Stub { - private final IBinder mInputChannelToken; + private final @NonNull IBinder mInputChannelToken; - InputMonitorHost(IBinder inputChannelToken) { + InputMonitorHost(@NonNull IBinder inputChannelToken) { mInputChannelToken = inputChannelToken; } diff --git a/services/core/java/com/android/server/locksettings/Android.bp b/services/core/java/com/android/server/locksettings/Android.bp deleted file mode 100644 index 53f1ac668e49..000000000000 --- a/services/core/java/com/android/server/locksettings/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -aconfig_declarations { - name: "locksettings_flags", - package: "com.android.server.locksettings", - container: "system", - srcs: ["*.aconfig"], -} - -java_aconfig_library { - name: "locksettings_flags_lib", - aconfig_declarations: "locksettings_flags", -} diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 820c0efcc1cf..4b704d01c3b9 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -428,15 +428,11 @@ class RebootEscrowManager { /** Wrapper function to set error code serialized through handler, */ private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) { - if (Flags.waitForInternetRor()) { - mInjector.post( - handler, - () -> { - mLoadEscrowDataErrorCode = value; - }); - } else { - mLoadEscrowDataErrorCode = value; - } + mInjector.post( + handler, + () -> { + mLoadEscrowDataErrorCode = value; + }); } /** Wrapper function to compare and set error code serialized through handler. */ @@ -511,23 +507,17 @@ class RebootEscrowManager { mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis()); } - if (Flags.waitForInternetRor()) { - // Timeout to stop retrying same as the wake lock timeout. - mInjector.postDelayed( - retryHandler, - () -> { - mRebootEscrowTimedOut = true; - }, - mInjector.getLoadEscrowTimeoutMillis()); - - mInjector.post( - retryHandler, - () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers)); - return; - } + // Timeout to stop retrying same as the wake lock timeout. + mInjector.postDelayed( + retryHandler, + () -> { + mRebootEscrowTimedOut = true; + }, + mInjector.getLoadEscrowTimeoutMillis()); - mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry( - retryHandler, 0, users, rebootEscrowUsers)); + mInjector.post( + retryHandler, + () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers)); } void scheduleLoadRebootEscrowDataOrFail( @@ -548,27 +538,13 @@ class RebootEscrowManager { return; } - if (Flags.waitForInternetRor()) { - if (mRebootEscrowTimedOut) { - Slog.w(TAG, "Failed to load reboot escrow data within timeout"); - compareAndSetLoadEscrowDataErrorCode( - ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler); - } else { - Slog.w( - TAG, - "Failed to load reboot escrow data after " + attemptNumber + " attempts"); - compareAndSetLoadEscrowDataErrorCode( - ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler); - } - onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); - return; - } - - Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); - if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { - mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; + if (mRebootEscrowTimedOut) { + Slog.w(TAG, "Failed to load reboot escrow data within timeout"); + compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler); } else { - mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; + Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); + compareAndSetLoadEscrowDataErrorCode( + ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler); } onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler); } diff --git a/services/core/java/com/android/server/locksettings/flags.aconfig b/services/core/java/com/android/server/locksettings/flags.aconfig deleted file mode 100644 index 6818de91c98e..000000000000 --- a/services/core/java/com/android/server/locksettings/flags.aconfig +++ /dev/null @@ -1,9 +0,0 @@ -package: "com.android.server.locksettings" -container: "system" - -flag { - name: "wait_for_internet_ror" - namespace: "sudo" - description: "Feature flag to wait for internet connectivity before calling resume on reboot server." - bug: "231660348" -}
\ No newline at end of file diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING index b33097c50002..7061044aaeee 100644 --- a/services/core/java/com/android/server/media/projection/TEST_MAPPING +++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING @@ -2,6 +2,34 @@ "presubmit": [ { "name": "MediaProjectionTests" + }, + { + "name": "CtsMediaProjectionTestCases" + }, + { + "name": "CtsMediaProjectionSDK33TestCases" + }, + { + "name": "CtsMediaProjectionSDK34TestCases" + }, + { + "name": "CtsMediaAudioTestCases", + "options": [ + { + "include-filter": "android.media.audio.cts.RemoteSubmixTest" + }, + { + "include-filter": "android.media.audio.cts.AudioPlaybackCaptureTest" + } + ] + }, + { + "name": "CtsStatsdAtomHostTestCases", + "options": [ + { + "include-filter": "android.cts.statsdatom.media.projection.MediaProjectionAtomsTests" + } + ] } ] } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 8d787fe99e3a..651111e431c3 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -985,8 +985,8 @@ public final class OverlayManagerService extends SystemService { final String pkgName = request.overlay.getPackageName(); if (callingUid != Process.ROOT_UID && !ArrayUtils.contains( mPackageManager.getPackagesForUid(callingUid), pkgName)) { - throw new IllegalArgumentException("UID " + callingUid + " does own package" - + "name " + pkgName); + throw new IllegalArgumentException("UID " + callingUid + " does not own " + + "packageName " + pkgName); } } else { // Enforce actor requirements for enabling, disabling, and reordering overlays. diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index b441e9dd561d..7e5ada54c953 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -1994,6 +1994,9 @@ public class ComputerEngine implements Computer { if (Process.isSdkSandboxUid(uid)) { uid = getBaseSdkSandboxUid(); } + if(isKnownIsolatedComputeApp(uid)) { + uid = getIsolatedOwner(uid); + } final int appId = UserHandle.getAppId(uid); return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp); } diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index f88681dbcaeb..e98176b0e82b 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -18,6 +18,7 @@ package com.android.server.pm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SpecialUsers.CanBeNULL; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.LauncherUserInfo; @@ -620,11 +621,17 @@ public abstract class UserManagerInternal { * Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL} * if there is no such user. */ - public abstract @UserIdInt int getCommunalProfileId(); + public abstract @CanBeNULL @UserIdInt int getCommunalProfileId(); /** - * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from - * background users. + * Returns the user id of the supervising profile, or {@link android.os.UserHandle#USER_NULL} if + * there is no such user. + */ + public abstract @CanBeNULL @UserIdInt int getSupervisingProfileId(); + + /** + * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from background + * users. */ public static boolean shouldShowNotificationForBackgroundUserSounds() { return Flags.addUiForSoundsFromBackgroundUsers() && Resources.getSystem().getBoolean( diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 33a7e7476cf6..053b4aae90dd 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1530,6 +1530,20 @@ public class UserManagerService extends IUserManager.Stub { return UserHandle.USER_NULL; } + /** Returns the currently-designated supervising profile, or USER_NULL if not present. */ + private @CanBeNULL @UserIdInt int getSupervisingProfileId() { + synchronized (mUsersLock) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { + final UserInfo user = mUsers.valueAt(i).info; + if (user.isSupervisingProfile() && !mRemovingUserIds.get(user.id)) { + return user.id; + } + } + } + return UserHandle.USER_NULL; + } + public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -8348,10 +8362,14 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public @UserIdInt int getCommunalProfileId() { + public @CanBeNULL @UserIdInt int getCommunalProfileId() { return getCommunalProfileIdUnchecked(); } + @Override + public @CanBeNULL @UserIdInt int getSupervisingProfileId() { + return UserManagerService.this.getSupervisingProfileId(); + } } // class LocalService diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java index e989d6875d15..4c14e96e6c98 100644 --- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java +++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java @@ -36,6 +36,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.DodecFunction; import com.android.internal.util.function.HexConsumer; @@ -141,150 +142,191 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe class AccessCheckDelegateImpl implements AccessCheckDelegate { public static final String SHELL_PKG = "com.android.shell"; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") private int mDelegateAndOwnerUid = INVALID_UID; @Nullable + @GuardedBy("mLock") private String mDelegatePackage; @Nullable + @GuardedBy("mLock") private String[] mDelegatePermissions; + @GuardedBy("mLock") boolean mDelegateAllPermissions; @Nullable + @GuardedBy("mLock") private SparseArray<ArrayMap<String, Integer>> mOverridePermissionStates; @Override public void setShellPermissionDelegate(int uid, @NonNull String packageName, @Nullable String[] permissions) { - mDelegateAndOwnerUid = uid; - mDelegatePackage = packageName; - mDelegatePermissions = permissions; - mDelegateAllPermissions = permissions == null; + synchronized (mLock) { + mDelegateAndOwnerUid = uid; + mDelegatePackage = packageName; + mDelegatePermissions = permissions; + mDelegateAllPermissions = permissions == null; + } PackageManager.invalidatePackageInfoCache(); } @Override public void removeShellPermissionDelegate() { - mDelegatePackage = null; - mDelegatePermissions = null; - mDelegateAllPermissions = false; + synchronized (mLock) { + mDelegatePackage = null; + mDelegatePermissions = null; + mDelegateAllPermissions = false; + } PackageManager.invalidatePackageInfoCache(); } @Override public void addOverridePermissionState(int ownerUid, int uid, @NonNull String permission, int state) { - if (mOverridePermissionStates == null) { - mDelegateAndOwnerUid = ownerUid; - mOverridePermissionStates = new SparseArray<>(); - } + synchronized (mLock) { + if (mOverridePermissionStates == null) { + mDelegateAndOwnerUid = ownerUid; + mOverridePermissionStates = new SparseArray<>(); + } - int uidIdx = mOverridePermissionStates.indexOfKey(uid); - ArrayMap<String, Integer> perUidOverrides; - if (uidIdx < 0) { - perUidOverrides = new ArrayMap<>(); - mOverridePermissionStates.put(uid, perUidOverrides); - } else { - perUidOverrides = mOverridePermissionStates.valueAt(uidIdx); - } + int uidIdx = mOverridePermissionStates.indexOfKey(uid); + ArrayMap<String, Integer> perUidOverrides; + if (uidIdx < 0) { + perUidOverrides = new ArrayMap<>(); + mOverridePermissionStates.put(uid, perUidOverrides); + } else { + perUidOverrides = mOverridePermissionStates.valueAt(uidIdx); + } - perUidOverrides.put(permission, state); + perUidOverrides.put(permission, state); + } PackageManager.invalidatePackageInfoCache(); } @Override public void removeOverridePermissionState(int uid, @NonNull String permission) { - if (mOverridePermissionStates == null) { - return; - } + synchronized (mLock) { + if (mOverridePermissionStates == null) { + return; + } - ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid); + ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid); - if (perUidOverrides == null) { - return; - } + if (perUidOverrides == null) { + return; + } - perUidOverrides.remove(permission); - PackageManager.invalidatePackageInfoCache(); + perUidOverrides.remove(permission); - if (perUidOverrides.isEmpty()) { - mOverridePermissionStates.remove(uid); - } - if (mOverridePermissionStates.size() == 0) { - mOverridePermissionStates = null; + if (perUidOverrides.isEmpty()) { + mOverridePermissionStates.remove(uid); + } + if (mOverridePermissionStates.size() == 0) { + mOverridePermissionStates = null; + } } + PackageManager.invalidatePackageInfoCache(); } @Override public void clearOverridePermissionStates(int uid) { - if (mOverridePermissionStates == null) { - return; - } + synchronized (mLock) { + if (mOverridePermissionStates == null) { + return; + } - mOverridePermissionStates.remove(uid); - PackageManager.invalidatePackageInfoCache(); + mOverridePermissionStates.remove(uid); - if (mOverridePermissionStates.size() == 0) { - mOverridePermissionStates = null; + if (mOverridePermissionStates.size() == 0) { + mOverridePermissionStates = null; + } } + PackageManager.invalidatePackageInfoCache(); } @Override public void clearAllOverridePermissionStates() { - mOverridePermissionStates = null; + synchronized (mLock) { + mOverridePermissionStates = null; + } PackageManager.invalidatePackageInfoCache(); } @Override public List<String> getDelegatedPermissionNames() { - return mDelegatePermissions == null ? null : List.of(mDelegatePermissions); + synchronized (mLock) { + return mDelegatePermissions == null ? null : List.of(mDelegatePermissions); + } } @Override public boolean hasShellPermissionDelegate() { - return mDelegateAllPermissions || mDelegatePermissions != null; + synchronized (mLock) { + return mDelegateAllPermissions || mDelegatePermissions != null; + } } @Override public boolean isDelegatePackage(int uid, @NonNull String packageName) { - return mDelegateAndOwnerUid == uid && TextUtils.equals(mDelegatePackage, packageName); + synchronized (mLock) { + return mDelegateAndOwnerUid == uid + && TextUtils.equals(mDelegatePackage, packageName); + } } @Override public boolean hasOverriddenPermissions() { - return mOverridePermissionStates != null; + synchronized (mLock) { + return mOverridePermissionStates != null; + } } @Override public boolean isDelegateAndOwnerUid(int uid) { - return uid == mDelegateAndOwnerUid; + synchronized (mLock) { + return uid == mDelegateAndOwnerUid; + } } @Override public boolean hasDelegateOrOverrides() { - return hasShellPermissionDelegate() || hasOverriddenPermissions(); + synchronized (mLock) { + return hasShellPermissionDelegate() || hasOverriddenPermissions(); + } } @Override public int checkPermission(@NonNull String packageName, @NonNull String permissionName, @NonNull String persistentDeviceId, @UserIdInt int userId, @NonNull QuadFunction<String, String, String, Integer, Integer> superImpl) { - if (TextUtils.equals(mDelegatePackage, packageName) && !SHELL_PKG.equals(packageName)) { - if (isDelegatePermission(permissionName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, - userId, superImpl); - } finally { - Binder.restoreCallingIdentity(identity); + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = !SHELL_PKG.equals(packageName) + && TextUtils.equals(mDelegatePackage, packageName) + && isDelegatePermission(permissionName); + + if (!useShellDelegate && mOverridePermissionStates != null) { + int uid = LocalServices.getService(PackageManagerInternal.class) + .getPackageUid(packageName, 0, userId); + if (uid >= 0) { + Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid); + if (permissionGrants != null + && permissionGrants.containsKey(permissionName)) { + return permissionGrants.get(permissionName); + } } } } - if (mOverridePermissionStates != null) { - int uid = LocalServices.getService(PackageManagerInternal.class) - .getPackageUid(packageName, 0, userId); - if (uid >= 0) { - Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid); - if (permissionGrants != null && permissionGrants.containsKey(permissionName)) { - return permissionGrants.get(permissionName); - } + + if (useShellDelegate) { + final long identity = Binder.clearCallingIdentity(); + try { + return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, userId, + superImpl); + } finally { + Binder.restoreCallingIdentity(identity); } } return superImpl.apply(packageName, permissionName, persistentDeviceId, userId); @@ -294,21 +336,27 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe public int checkUidPermission(int uid, @NonNull String permissionName, @NonNull String persistentDeviceId, @NonNull TriFunction<Integer, String, String, Integer> superImpl) { - if (uid == mDelegateAndOwnerUid && uid != Process.SHELL_UID) { - if (isDelegatePermission(permissionName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return checkUidPermission(Process.SHELL_UID, permissionName, - persistentDeviceId, superImpl); - } finally { - Binder.restoreCallingIdentity(identity); + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid != Process.SHELL_UID && uid == mDelegateAndOwnerUid + && isDelegatePermission(permissionName); + + if (!useShellDelegate && mOverridePermissionStates != null) { + Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid); + if (permissionGrants != null && permissionGrants.containsKey(permissionName)) { + return permissionGrants.get(permissionName); } } } - if (mOverridePermissionStates != null) { - Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid); - if (permissionGrants != null && permissionGrants.containsKey(permissionName)) { - return permissionGrants.get(permissionName); + + if (useShellDelegate) { + final long identity = Binder.clearCallingIdentity(); + try { + return checkUidPermission(Process.SHELL_UID, permissionName, persistentDeviceId, + superImpl); + } finally { + Binder.restoreCallingIdentity(identity); } } return superImpl.apply(uid, permissionName, persistentDeviceId); @@ -319,7 +367,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Nullable String attributionTag, int virtualDeviceId, boolean raw, @NonNull HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer> superImpl) { - if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -335,7 +389,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Override public int checkAudioOperation(int code, int usage, int uid, @Nullable String packageName, @NonNull QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { - if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -354,7 +414,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Nullable String message, boolean shouldCollectMessage, int notedCount, @NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String, Boolean, Integer, SyncNotedAppOp> superImpl) { - if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -375,21 +441,29 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) { - if (!isDelegateOp(code)) { - return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, - message, shouldCollectMessage, skiProxyOperation); + boolean isDelegateOp; + int delegateAndOwnerUid; + + synchronized (mLock) { + isDelegateOp = isDelegateOp(code); + delegateAndOwnerUid = mDelegateAndOwnerUid; + } + + if (!isDelegateOp) { + return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message, + shouldCollectMessage, skiProxyOperation); } final int shellUid = UserHandle.getUid( UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID); AttributionSource next = attributionSource.getNext(); - if (next != null && next.getUid() == mDelegateAndOwnerUid) { + if (next != null && next.getUid() == delegateAndOwnerUid) { next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null, next.getDeviceId(), next.getNext()); attributionSource = new AttributionSource(attributionSource, next); } - if (attributionSource.getUid() == mDelegateAndOwnerUid) { + if (attributionSource.getUid() == delegateAndOwnerUid) { attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, @@ -397,9 +471,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe } final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, attributionSource, - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - skiProxyOperation); + return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message, + shouldCollectMessage, skiProxyOperation); } finally { Binder.restoreCallingIdentity(identity); } @@ -413,7 +486,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @AttributionFlags int attributionFlags, int attributionChainId, @NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean, Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) { - if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -440,7 +519,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String, Boolean, Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl) { - if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid + && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId( attributionSource.getUid()), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -467,7 +553,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @NonNull AttributionSource attributionSource, boolean skipProxyOperation, @NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean, Void> superImpl) { - if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid + && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId( attributionSource.getUid()), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); @@ -490,7 +583,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe public void finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, Integer, String, String, Integer> superImpl) { - if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + boolean useShellDelegate; + + synchronized (mLock) { + useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code); + } + + if (useShellDelegate) { final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2744721c3a46..e9f522d08328 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -86,6 +86,7 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver import static com.android.hardware.input.Flags.enableNew25q2Keycodes; import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures; import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures; +import static com.android.hardware.input.Flags.fixSearchModifierFallbacks; import static com.android.hardware.input.Flags.inputManagerLifecycleSupport; import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.modifierShortcutDump; @@ -4181,6 +4182,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } + if (fixSearchModifierFallbacks()) { + // Pass event as unhandled to give other services, e.g. InputManagerService, the + // opportunity to determine if the event can be modified, e.g. generating a fallback for + // meta/search events. + return false; + } + // Reserve all the META modifier combos for system behavior return (metaState & KeyEvent.META_META_ON) != 0; } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 5ee9b7d09fdd..46c497d04f9e 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -176,7 +176,9 @@ public class ThermalManagerService extends SystemService { try { final HeadroomCallbackData data; synchronized (mTemperatureWatcher.mSamples) { - Slog.d(TAG, "Updating skin threshold: " + threshold); + if (DEBUG) { + Slog.d(TAG, "Updating skin threshold: " + threshold); + } mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true); data = mTemperatureWatcher.getHeadroomCallbackDataLocked(); } @@ -454,7 +456,9 @@ public class ThermalManagerService extends SystemService { && temperature.getType() == Temperature.TYPE_SKIN) { final HeadroomCallbackData data; synchronized (mTemperatureWatcher.mSamples) { - Slog.d(TAG, "Updating new temperature: " + temperature); + if (DEBUG) { + Slog.d(TAG, "Updating new temperature: " + temperature); + } mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(), temperature); mTemperatureWatcher.mCachedHeadrooms.clear(); @@ -1878,6 +1882,7 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS; + @GuardedBy("mSamples") private final Handler mHandler = BackgroundThread.getHandler(); /** @@ -1900,6 +1905,9 @@ public class ThermalManagerService extends SystemService { @GuardedBy("mSamples") private long mLastForecastCallTimeMillis = 0; + private final Runnable mGetAndUpdateTemperatureSamplesRunnable = + this::getAndUpdateTemperatureSamples; + void getAndUpdateThresholds() { List<TemperatureThreshold> thresholds = mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN); @@ -1930,7 +1938,9 @@ public class ThermalManagerService extends SystemService { return; } if (override) { - Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold); + if (DEBUG) { + Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold); + } mCachedHeadrooms.clear(); Arrays.fill(mHeadroomThresholds, Float.NaN); } @@ -1962,7 +1972,7 @@ public class ThermalManagerService extends SystemService { < mInactivityThresholdMillis) { // Trigger this again after a second as long as forecast has been called more // recently than the inactivity timeout - mHandler.postDelayed(this::getAndUpdateTemperatureSamples, 1000); + mHandler.postDelayed(mGetAndUpdateTemperatureSamplesRunnable, 1000); } else { // Otherwise, we've been idle for at least 10 seconds, so we should // shut down @@ -1974,6 +1984,9 @@ public class ThermalManagerService extends SystemService { long now = SystemClock.elapsedRealtime(); final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true, Temperature.TYPE_SKIN); + if (DEBUG) { + Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures); + } for (Temperature temperature : temperatures) { updateTemperatureSampleLocked(now, temperature); } @@ -2080,10 +2093,16 @@ public class ThermalManagerService extends SystemService { } synchronized (mSamples) { mLastForecastCallTimeMillis = SystemClock.elapsedRealtime(); - if (mSamples.isEmpty()) { + if (!mHandler.hasCallbacks(mGetAndUpdateTemperatureSamplesRunnable)) { + if (DEBUG) { + Slog.d(TAG, "No temperature update callback, scheduling one"); + } getAndUpdateTemperatureSamples(); + } else { + if (DEBUG) { + Slog.d(TAG, "Temperature update callback already exists"); + } } - // If somehow things take much longer than expected or there are no temperatures // to sample, return early if (mSamples.isEmpty()) { @@ -2103,8 +2122,11 @@ public class ThermalManagerService extends SystemService { Binder.getCallingUid(), FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS, headroom, forecastSeconds); - Slog.d(TAG, "Headroom forecast in " + forecastSeconds + "s served from cache: " - + headroom); + if (DEBUG) { + Slog.d(TAG, + "Headroom forecast in " + forecastSeconds + "s served from cache: " + + headroom); + } return headroom; } @@ -2133,7 +2155,10 @@ public class ThermalManagerService extends SystemService { Binder.getCallingUid(), FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS, headroom, 0); - Slog.d(TAG, "Headroom forecast in 0s served from cache: " + headroom); + if (DEBUG) { + Slog.d(TAG, + "Headroom forecast in 0s served from cache: " + headroom); + } return headroom; } // Don't try to forecast, just use the latest one we have @@ -2182,7 +2207,9 @@ public class ThermalManagerService extends SystemService { getForecast(DEFAULT_FORECAST_SECONDS), DEFAULT_FORECAST_SECONDS, Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length)); - Slog.d(TAG, "New headroom callback data: " + data); + if (DEBUG) { + Slog.d(TAG, "New headroom callback data: " + data); + } return data; } 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 8c3b7c606f04..3ece07c84080 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -17,6 +17,7 @@ package com.android.server.security.advancedprotection; import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; +import static android.provider.Settings.Secure.AAPM_USB_DATA_PROTECTION; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import android.Manifest; @@ -69,6 +70,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** @hide */ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { @@ -129,7 +131,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub Slog.e(TAG, "Failed to initialize DisallowCellular2g", e); } } - if (android.security.Flags.aapmFeatureUsbDataProtection()) { + if (android.security.Flags.aapmFeatureUsbDataProtection() + // Usb data protection is enabled by default + && mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) + == AdvancedProtectionStore.ON) { try { mHooks.add(new UsbDataAdvancedProtectionHook(mContext, enabled)); } catch (Exception e) { @@ -183,7 +188,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub // Without permission check private boolean isAdvancedProtectionEnabledInternal() { - return mStore.retrieve(); + return mStore.retrieveAdvancedProtectionModeEnabled(); } @Override @@ -217,7 +222,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub try { synchronized (mCallbacks) { if (enabled != isAdvancedProtectionEnabledInternal()) { - mStore.store(enabled); + mStore.storeAdvancedProtectionModeEnabled(enabled); sendModeChanged(enabled); logAdvancedProtectionEnabled(enabled); } @@ -227,6 +232,34 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub } } + public void setUsbDataProtectionEnabled(boolean enabled) { + int value = enabled ? AdvancedProtectionStore.ON + : AdvancedProtectionStore.OFF; + setAdvancedProtectionSubSettingInt(AAPM_USB_DATA_PROTECTION, value); + } + + private void setAdvancedProtectionSubSettingInt(String key, int value) { + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mCallbacks) { + mStore.storeInt(key, value); + Slog.i(TAG, "Advanced protection: subsetting" + key + " is " + value); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public boolean isUsbDataProtectionEnabled() { + final long identity = Binder.clearCallingIdentity(); + try { + return mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) + == AdvancedProtectionStore.ON; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type, @@ -419,8 +452,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub @VisibleForTesting static class AdvancedProtectionStore { private final Context mContext; - private static final int APM_ON = 1; - private static final int APM_OFF = 0; + static final int ON = 1; + static final int OFF = 0; private final UserManagerInternal mUserManager; AdvancedProtectionStore(@NonNull Context context) { @@ -428,15 +461,26 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub mUserManager = LocalServices.getService(UserManagerInternal.class); } - void store(boolean enabled) { + void storeAdvancedProtectionModeEnabled(boolean enabled) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, enabled ? ON : OFF, + mUserManager.getMainUserId()); + } + + boolean retrieveAdvancedProtectionModeEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, OFF, mUserManager.getMainUserId()) == ON; + } + + void storeInt(String key, int value) { Settings.Secure.putIntForUser(mContext.getContentResolver(), - ADVANCED_PROTECTION_MODE, enabled ? APM_ON : APM_OFF, + key, value, mUserManager.getMainUserId()); } - boolean retrieve() { + int retrieveInt(String key, int defaultValue) { return Settings.Secure.getIntForUser(mContext.getContentResolver(), - ADVANCED_PROTECTION_MODE, APM_OFF, mUserManager.getMainUserId()) == APM_ON; + key, defaultValue, mUserManager.getMainUserId()); } } diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java index 42505ad2de3f..ae17a459010b 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java @@ -45,6 +45,10 @@ class AdvancedProtectionShellCommand extends ShellCommand { return setProtectionEnabled(); case "is-protection-enabled": return isProtectionEnabled(pw); + case "set-usb-data-protection-enabled": + return setUsbDataProtectedEnabled(); + case "is-usb-data-protection-enabled": + return isUsbDataProtectedEnabled(pw); } } catch (RemoteException e) { pw.println("Remote exception: " + e); @@ -64,6 +68,10 @@ class AdvancedProtectionShellCommand extends ShellCommand { pw.println(" Print this help text."); pw.println(" set-protection-enabled [true|false]"); pw.println(" is-protection-enabled"); + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + pw.println(" set-usb-data-protection-enabled [true|false]"); + pw.println(" is-usb-data-protection-enabled"); + } } @SuppressLint("AndroidFrameworkRequiresPermission") @@ -79,4 +87,22 @@ class AdvancedProtectionShellCommand extends ShellCommand { pw.println(protectionMode); return 0; } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int setUsbDataProtectedEnabled() throws RemoteException { + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + String protectionMode = getNextArgRequired(); + mService.setUsbDataProtectionEnabled(Boolean.parseBoolean(protectionMode)); + } + return 0; + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int isUsbDataProtectedEnabled(@NonNull PrintWriter pw) throws RemoteException { + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + boolean protectionMode = mService.isUsbDataProtectionEnabled(); + pw.println(protectionMode); + } + return 0; + } } diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java index cb82f480d73b..d152a1dbe17d 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java @@ -35,6 +35,7 @@ import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.NonNull; import android.annotation.Nullable; +import android.window.DesktopModeFlags; import com.android.server.wm.utils.OptPropFactory; import com.android.window.flags.Flags; @@ -177,7 +178,7 @@ class AppCompatCameraOverrides { * </ul> */ boolean shouldApplyFreeformTreatmentForCameraCompat() { - return Flags.enableCameraCompatForDesktopWindowing() + return DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue() && (shouldEnableCameraCompatFreeformTreatmentForApp() || shouldEnableCameraCompatFreeformTreatmentForAllApps()); } diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java index 276c7d2cbaa0..e7a0803df916 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java @@ -26,9 +26,9 @@ import android.app.CameraCompatTaskInfo; import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.widget.Toast; +import android.window.DesktopModeFlags; import com.android.internal.annotations.VisibleForTesting; -import com.android.window.flags.Flags; /** * Encapsulate policy logic related to app compat display rotation. @@ -53,7 +53,7 @@ class AppCompatCameraPolicy { final boolean needsDisplayRotationCompatPolicy = wmService.mAppCompatConfiguration.isCameraCompatTreatmentEnabledAtBuildTime(); final boolean needsCameraCompatFreeformPolicy = - Flags.enableCameraCompatForDesktopWindowing() + DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue() && DesktopModeHelper.canEnterDesktopMode(wmService.mContext); if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) { mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH); diff --git a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java index 937039d838e5..a7d03485058f 100644 --- a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java +++ b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java @@ -29,13 +29,13 @@ import java.util.function.LongSupplier; /** * Logs potential race conditions that lead to incorrect auto-rotate setting. * - * Before go/auto-rotate-refactor, there is a race condition that happen during device state + * <p>Before go/auto-rotate-refactor, there is a race condition that happen during device state * changes, as a result, incorrect auto-rotate setting are written for a device state in * DEVICE_STATE_ROTATION_LOCK. Realistically, users shouldn’t be able to change * DEVICE_STATE_ROTATION_LOCK while the device folds/unfolds. * - * This class monitors the time between a device state change and a subsequent change to the device - * state based auto-rotate setting. If the duration is less than a threshold + * <p>This class monitors the time between a device state change and a subsequent change to the + * device state based auto-rotate setting. If the duration is less than a threshold * (DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD), a potential issue is logged. The logging of * the atom is not expected to occur often, realistically estimated once a month on few devices. * But the number could be bigger, as that's what this metric is set to reveal. @@ -72,23 +72,33 @@ public class DeviceStateAutoRotateSettingIssueLogger { } private void onStateChange() { - // Only move forward if both of the events have occurred already - if (mLastDeviceStateChangeTime != TIME_NOT_SET - && mLastDeviceStateAutoRotateSettingChangeTime != TIME_NOT_SET) { - final long duration = - mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime; - boolean isDeviceStateChangeFirst = duration > 0; + // Only move forward if both of the events have occurred already. + if (mLastDeviceStateChangeTime == TIME_NOT_SET + || mLastDeviceStateAutoRotateSettingChangeTime == TIME_NOT_SET) { + return; + } + final long duration = + mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime; + boolean isDeviceStateChangeFirst = duration > 0; - if (abs(duration) - < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) { - FrameworkStatsLog.write( - FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED, - (int) abs(duration), - isDeviceStateChangeFirst); - } + if (abs(duration) + < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) { + FrameworkStatsLog.write( + FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED, + (int) abs(duration), + isDeviceStateChangeFirst); + // This pair is logged, reset both timestamps. mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET; mLastDeviceStateChangeTime = TIME_NOT_SET; + } else { + // This pair was not logged, reset the earlier timestamp. + if (isDeviceStateChangeFirst) { + mLastDeviceStateChangeTime = TIME_NOT_SET; + } else { + mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET; + } } + } } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 51c3da098020..02b53b0106b8 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -512,9 +512,14 @@ class TransitionController { return false; } + /** Returns {@code true} if the display contains a collecting transition. */ + boolean isCollectingTransitionOnDisplay(@NonNull DisplayContent dc) { + return mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc); + } + /** Returns {@code true} if the display contains a running or pending transition. */ boolean isTransitionOnDisplay(@NonNull DisplayContent dc) { - if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) { + if (isCollectingTransitionOnDisplay(dc)) { return true; } for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a9bb690d4e53..3ccbc868377e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1845,9 +1845,12 @@ public class WindowManagerService extends IWindowManager.Stub // Only a presentation window needs a transition because its visibility affets the // lifecycle of apps below (b/390481865). if (enablePresentationForConnectedDisplays() && win.isPresentation()) { - Transition transition = null; + final boolean wasTransitionOnDisplay = + win.mTransitionController.isCollectingTransitionOnDisplay(displayContent); + Transition newlyCreatedTransition = null; if (!win.mTransitionController.isCollecting()) { - transition = win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN); + newlyCreatedTransition = + win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN); } win.mTransitionController.collect(win.mToken); res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, @@ -1856,9 +1859,14 @@ public class WindowManagerService extends IWindowManager.Stub // A presentation hides all activities behind on the same display. win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, /*notifyClients=*/ true); - win.mTransitionController.getCollectingTransition().setReady(win.mToken, true); - if (transition != null) { - win.mTransitionController.requestStartTransition(transition, null, + if (!wasTransitionOnDisplay && win.mTransitionController + .isCollectingTransitionOnDisplay(displayContent)) { + // Set the display ready only when the display gets added to the collecting + // transition in this operation. + win.mTransitionController.setReady(win.mToken); + } + if (newlyCreatedTransition != null) { + win.mTransitionController.requestStartTransition(newlyCreatedTransition, null, null /* remoteTransition */, null /* displayChange */); } } else { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 22ddd5f39b24..d43aba0d218d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2365,9 +2365,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Only a presentation window needs a transition because its visibility affets the // lifecycle of apps below (b/390481865). if (enablePresentationForConnectedDisplays() && isPresentation()) { - Transition transition = null; + final boolean wasTransitionOnDisplay = + mTransitionController.isCollectingTransitionOnDisplay(displayContent); + Transition newlyCreatedTransition = null; if (!mTransitionController.isCollecting()) { - transition = mTransitionController.createAndStartCollecting(TRANSIT_CLOSE); + newlyCreatedTransition = + mTransitionController.createAndStartCollecting(TRANSIT_CLOSE); } mTransitionController.collect(mToken); mAnimatingExit = true; @@ -2376,9 +2379,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // A presentation hides all activities behind on the same display. mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, /*notifyClients=*/ true); - mTransitionController.getCollectingTransition().setReady(mToken, true); - if (transition != null) { - mTransitionController.requestStartTransition(transition, null, + if (!wasTransitionOnDisplay && mTransitionController + .isCollectingTransitionOnDisplay(displayContent)) { + // Set the display ready only when the display gets added to the collecting + // transition in this operation. + mTransitionController.setReady(mToken); + } + if (newlyCreatedTransition != null) { + mTransitionController.requestStartTransition(newlyCreatedTransition, null, null /* remoteTransition */, null /* displayChange */); } } else { diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index ec8794f8073f..017284cded8e 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1978,6 +1978,11 @@ NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token, return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP; } + // -2 : Skip sending even to application and go directly to post processing e.g. fallbacks. + if (delayMillis == -2) { + return inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK; + } + return milliseconds_to_nanoseconds(delayMillis); } diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java index bb6339c79502..0b5a95b0e888 100644 --- a/services/supervision/java/com/android/server/supervision/SupervisionService.java +++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; +import android.app.KeyguardManager; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.supervision.ISupervisionManager; @@ -147,9 +148,21 @@ public class SupervisionService extends ISupervisionManager.Stub { @Override @Nullable public Intent createConfirmSupervisionCredentialsIntent() { - // TODO(b/392961554): (1) Return null if supervision is not enabled. - // (2) check if PIN exists before return a valid intent. enforceAnyPermission(QUERY_USERS, MANAGE_USERS); + if (!isSupervisionEnabledForUser(mContext.getUserId())) { + return null; + } + // Verify the supervising user profile exists and has a secure credential set. + final int supervisingUserId = mInjector.getUserManagerInternal().getSupervisingProfileId(); + final long token = Binder.clearCallingIdentity(); + try { + if (supervisingUserId == UserHandle.USER_NULL + || !mInjector.getKeyguardManager().isDeviceSecure(supervisingUserId)) { + return null; + } + } finally { + Binder.restoreCallingIdentity(token); + } final Intent intent = new Intent(ACTION_CONFIRM_SUPERVISION_CREDENTIALS); // explicitly set the package for security intent.setPackage("com.android.settings"); @@ -277,6 +290,7 @@ public class SupervisionService extends ISupervisionManager.Stub { static class Injector { private final Context mContext; private DevicePolicyManagerInternal mDpmInternal; + private KeyguardManager mKeyguardManager; private PackageManager mPackageManager; private UserManagerInternal mUserManagerInternal; @@ -292,6 +306,13 @@ public class SupervisionService extends ISupervisionManager.Stub { return mDpmInternal; } + KeyguardManager getKeyguardManager() { + if (mKeyguardManager == null) { + mKeyguardManager = mContext.getSystemService(KeyguardManager.class); + } + return mKeyguardManager; + } + PackageManager getPackageManager() { if (mPackageManager == null) { mPackageManager = mContext.getPackageManager(); diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java index 554b5b4297f2..740424813c2a 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java @@ -98,9 +98,9 @@ public class UserDataPreparerTest { File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID); systemDeDir.mkdirs(); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE); - verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), + verify(mStorageManagerMock).prepareUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_DE)); - verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID), + verify(mInstaller).createUserData(isNull(), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE)); int serialNumber = UserDataPreparer.getSerialNumber(userDeDir); assertEquals(TEST_USER_SERIAL, serialNumber); @@ -115,9 +115,9 @@ public class UserDataPreparerTest { File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID); systemCeDir.mkdirs(); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); - verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), + verify(mStorageManagerMock).prepareUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); - verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID), + verify(mInstaller).createUserData(isNull(), eq(TEST_USER_ID), eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE)); int serialNumber = UserDataPreparer.getSerialNumber(userCeDir); assertEquals(TEST_USER_SERIAL, serialNumber); @@ -129,10 +129,10 @@ public class UserDataPreparerTest { public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception { TEST_USER.lastLoggedInTime = 0; doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock) - .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), + .prepareUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); - verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), + verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); } @@ -140,10 +140,10 @@ public class UserDataPreparerTest { public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception { TEST_USER.lastLoggedInTime = System.currentTimeMillis(); doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock) - .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), + .prepareUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE); - verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class), + verify(mStorageManagerMock, never()).destroyUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); } @@ -171,9 +171,9 @@ public class UserDataPreparerTest { mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE); - verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID), + verify(mInstaller).destroyUserData(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_DE)); - verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), + verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_DE)); // systemDir (normal path: /data/system/users/$userId) should have been deleted. @@ -195,9 +195,9 @@ public class UserDataPreparerTest { mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_CE); - verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID), + verify(mInstaller).destroyUserData(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); - verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), + verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); // systemCeDir (normal path: /data/system_ce/$userId) should still exist but be empty, since @@ -225,7 +225,7 @@ public class UserDataPreparerTest { .reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL, Arrays.asList(u1, u2), Arrays.asList(dir1, dir2, dir3)); // Verify that user 3 data is removed - verify(mInstaller).destroyUserData(isNull(String.class), eq(3), + verify(mInstaller).destroyUserData(isNull(), eq(3), eq(StorageManager.FLAG_STORAGE_DE|StorageManager.FLAG_STORAGE_CE)); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java index 3289d70b89ac..fe4baeb80ee7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java @@ -79,7 +79,8 @@ import java.util.ArrayList; public class ApplicationStartInfoTest { private static final String TAG = ApplicationStartInfoTest.class.getSimpleName(); - private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo"); + private static final ComponentName COMPONENT = + new ComponentName("com.android.test", "com.android.test.Foo"); private static final int APP_1_UID = 10123; private static final int APP_1_PID_1 = 12345; diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 067fba9893e5..64e6d323bdfd 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -93,7 +93,6 @@ android_test { "net_flags_lib", "CtsVirtualDeviceCommonLib", "com_android_server_accessibility_flags_lib", - "locksettings_flags_lib", ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), { "true": ["service-crashrecovery-pre-jarjar"], default: [], diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index e0bc3e76f31d..457d8a96fea4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -87,6 +87,9 @@ import android.content.res.XmlResourceParser; import android.graphics.drawable.Icon; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; +import android.hardware.input.IInputManager; +import android.hardware.input.InputManager; +import android.hardware.input.InputManagerGlobal; import android.hardware.input.KeyGestureEvent; import android.net.Uri; import android.os.Build; @@ -237,6 +240,9 @@ public class AccessibilityManagerServiceTest { @Mock private HearingDevicePhoneCallNotificationController mMockHearingDevicePhoneCallNotificationController; + @Mock + private IInputManager mMockInputManagerService; + private InputManagerGlobal.TestSession mInputManagerTestSession; @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback; @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor; private IAccessibilityManager mA11yManagerServiceOnDevice; @@ -270,6 +276,10 @@ public class AccessibilityManagerServiceTest { mInputFilter = mock(FakeInputFilter.class); mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager); + mInputManagerTestSession = InputManagerGlobal.createTestSession(mMockInputManagerService); + InputManager mockInputManager = new InputManager(mTestableContext); + mTestableContext.addMockSystemService(InputManager.class, mockInputManager); + when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn( new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")); when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(), @@ -334,6 +344,9 @@ public class AccessibilityManagerServiceTest { FieldSetter.setField( am, AccessibilityManager.class.getDeclaredField("mService"), mA11yManagerServiceOnDevice); + if (mInputManagerTestSession != null) { + mInputManagerTestSession.close(); + } } private void setupAccessibilityServiceConnection(int serviceInfoFlag) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java index d94faec4cf01..49eb63b2c261 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java @@ -28,10 +28,6 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.PointF; -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.view.Display; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -54,9 +50,6 @@ import java.util.List; */ public class TwoFingersDownOrSwipeTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final float DEFAULT_X = 100f; private static final float DEFAULT_Y = 100f; @@ -94,22 +87,6 @@ public class TwoFingersDownOrSwipeTest { } @Test - @RequiresFlagsDisabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION) - public void sendTwoFingerDownEvent_onGestureCompleted_withoutCopiedEvents() { - final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY, - new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10)); - - for (MotionEvent event : downEvents) { - mGesturesObserver.onMotionEvent(event, event, 0); - } - - verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted( - MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, downEvents.get(1), - downEvents.get(1), 0); - } - - @Test - @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION) public void sendTwoFingerDownEvent_onGestureCompleted() { final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY, new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10)); 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 c50c62323212..a1f73170e549 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -816,14 +816,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkIntentAction( DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED), MockUtils.checkUserHandle(CALLER_USER_HANDLE), - isNull(String.class), + isNull(), eq(AppOpsManager.OP_NONE), any(Bundle.class), any(BroadcastReceiver.class), eq(dpms.mHandler), eq(Activity.RESULT_OK), - isNull(String.class), - isNull(Bundle.class)); + isNull(), + isNull()); assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( @@ -873,14 +873,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkIntentAction( DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED), MockUtils.checkUserHandle(CALLER_USER_HANDLE), - isNull(String.class), + isNull(), eq(AppOpsManager.OP_NONE), any(Bundle.class), any(BroadcastReceiver.class), eq(dpms.mHandler), eq(Activity.RESULT_OK), - isNull(String.class), - isNull(Bundle.class)); + isNull(), + isNull()); assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index ae781dcb834a..52c7c48cc8b7 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -60,10 +60,6 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; 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 androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -74,7 +70,6 @@ import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnReb import com.android.server.pm.UserManagerInternal; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -113,9 +108,6 @@ public class RebootEscrowManagerTests { 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23, }; - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private Context mContext; private UserManager mUserManager; private UserManagerInternal mUserManagerInternal; @@ -847,53 +839,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsDisabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) - public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception { - setServerBasedRebootEscrowProvider(); - - when(mInjected.getBootCount()).thenReturn(0); - RebootEscrowListener mockListener = mock(RebootEscrowListener.class); - mService.setRebootEscrowListener(mockListener); - mService.prepareRebootEscrow(); - - clearInvocations(mServiceConnection); - callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID); - verify(mockListener).onPreparedForReboot(eq(true)); - verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); - - // Use x -> x for both wrap & unwrap functions. - when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) - .thenAnswer(invocation -> invocation.getArgument(0)); - assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); - verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); - assertTrue(mStorage.hasRebootEscrowServerBlob()); - - // pretend reboot happens here - when(mInjected.getBootCount()).thenReturn(1); - ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing() - .when(mInjected) - .reportMetric( - metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), - eq(2) /* Server based */, - eq(2) /* attempt count */, - anyInt(), - eq(0) /* vbmeta status */, - anyInt()); - when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); - - mService.loadRebootEscrowDataIfAvailable(mHandler); - // Sleep 5s for the retry to complete - Thread.sleep(5 * 1000); - assertFalse(metricsSuccessCaptor.getValue()); - assertEquals( - Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), - metricsErrorCodeCaptor.getValue()); - } - - @Test public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception { setServerBasedRebootEscrowProvider(); @@ -941,7 +886,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable() throws Exception { setServerBasedRebootEscrowProvider(); @@ -989,7 +933,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception { setServerBasedRebootEscrowProvider(); @@ -1044,7 +987,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay() throws Exception { setServerBasedRebootEscrowProvider(); @@ -1103,7 +1045,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted() throws Exception { setServerBasedRebootEscrowProvider(); @@ -1163,7 +1104,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted() throws Exception { setServerBasedRebootEscrowProvider(); @@ -1219,7 +1159,6 @@ public class RebootEscrowManagerTests { } @Test - @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR) public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess() throws Exception { setServerBasedRebootEscrowProvider(); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java index 88395a4889c4..071bd739072b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java @@ -306,9 +306,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { // This method is always called, even with PI == null. if (resultIntent == null) { - verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(isNull()); } else { - verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(notNull()); } runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { @@ -619,7 +619,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { makeResultIntent())); // The intent should be sent right away. - verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(notNull()); }); // Already pinned. @@ -661,7 +661,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertTrue(request.accept()); // The intent is only sent once, so times(1). - verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(isNull()); }); // Still pinned. @@ -698,7 +698,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { makeResultIntent())); // The intent should be sent right away. - verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(notNull()); }); // Already pinned. @@ -742,7 +742,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertTrue(request.accept()); // The intent is only sent once, so times(1). - verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class)); + verify(mServiceContext, times(1)).sendIntentSender(isNull()); }); // Still pinned. diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 952d8fa47a34..09acfddacf03 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -51,6 +51,7 @@ import android.os.IThermalStatusListener; import android.os.PowerManager; import android.os.RemoteException; import android.os.Temperature; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -78,6 +79,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server @@ -117,7 +119,8 @@ public class ThermalManagerServiceTest { */ private class ThermalHalFake extends ThermalHalWrapper { private static final int INIT_STATUS = Temperature.THROTTLING_NONE; - private List<Temperature> mTemperatureList = new ArrayList<>(); + private final List<Temperature> mTemperatureList = new ArrayList<>(); + private AtomicInteger mGetCurrentTemperaturesCalled = new AtomicInteger(); private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); @@ -173,6 +176,7 @@ public class ThermalManagerServiceTest { mTemperatureList.add(mUsbPort); mCoolingDeviceList.add(mCpu); mCoolingDeviceList.add(mGpu); + mGetCurrentTemperaturesCalled.set(0); } void enableForecastSkinTemperature() { @@ -188,14 +192,24 @@ public class ThermalManagerServiceTest { mForecastSkinTemperaturesError = true; } + void updateTemperatureList(Temperature... temperatures) { + synchronized (mTemperatureList) { + mTemperatureList.clear(); + mTemperatureList.addAll(Arrays.asList(temperatures)); + } + } + @Override protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) { List<Temperature> ret = new ArrayList<>(); - for (Temperature temperature : mTemperatureList) { - if (shouldFilter && type != temperature.getType()) { - continue; + synchronized (mTemperatureList) { + mGetCurrentTemperaturesCalled.incrementAndGet(); + for (Temperature temperature : mTemperatureList) { + if (shouldFilter && type != temperature.getType()) { + continue; + } + ret.add(temperature); } - ret.add(temperature); } return ret; } @@ -407,7 +421,7 @@ public class ThermalManagerServiceTest { Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC); resetListenerMock(); int status = Temperature.THROTTLING_SEVERE; - mFakeHal.mTemperatureList = new ArrayList<>(); + mFakeHal.updateTemperatureList(); // Should not notify on non-skin type Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status); @@ -537,6 +551,42 @@ public class ThermalManagerServiceTest { } @Test + @DisableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) + public void testGetThermalHeadroom_handlerUpdateTemperatures() + throws RemoteException, InterruptedException { + // test that handler will at least enqueue one message to periodically read temperatures + // even if there is sample seeded from HAL temperature callback + String temperatureName = "skin1"; + Temperature temperature = new Temperature(100, Temperature.TYPE_SKIN, temperatureName, + Temperature.THROTTLING_NONE); + mFakeHal.mCallback.onTemperatureChanged(temperature); + float headroom = mService.mService.getThermalHeadroom(0); + // the callback temperature 100C (headroom > 1.0f) sample should have been appended by the + // immediately scheduled fake HAL current temperatures read (mSkin1, mSkin2), and because + // there are less samples for prediction, the latest temperature mSkin1 is used to calculate + // headroom (mSkin2 has no threshold), which is 0.6f (28C vs threshold 40C). + assertEquals(0.6f, headroom, 0.01f); + // one called by service onActivityManagerReady, one called by handler on headroom call + assertEquals(2, mFakeHal.mGetCurrentTemperaturesCalled.get()); + // periodic read should update the samples history, so the headroom should increase 0.1f + // as current temperature goes up by 3C every 1100ms. + for (int i = 1; i < 5; i++) { + Temperature newTemperature = new Temperature(mFakeHal.mSkin1.getValue() + 3 * i, + Temperature.TYPE_SKIN, + temperatureName, + Temperature.THROTTLING_NONE); + mFakeHal.updateTemperatureList(newTemperature); + // wait for handler to update temperature + Thread.sleep(1100); + // assert that only one callback was scheduled to query HAL when making multiple + // headroom calls + assertEquals(2 + i, mFakeHal.mGetCurrentTemperaturesCalled.get()); + headroom = mService.mService.getThermalHeadroom(0); + assertEquals(0.6f + 0.1f * i, headroom, 0.01f); + } + } + + @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java index 339bac4f768b..d6b3fecb487c 100644 --- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java @@ -45,6 +45,8 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; @SuppressLint("VisibleForTests") @@ -66,17 +68,28 @@ public class AdvancedProtectionServiceTest { mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { + private Map<String, Integer> mStoredValues = new HashMap<>(); private boolean mEnabled = false; @Override - boolean retrieve() { + boolean retrieveAdvancedProtectionModeEnabled() { return mEnabled; } @Override - void store(boolean enabled) { + void storeAdvancedProtectionModeEnabled(boolean enabled) { this.mEnabled = enabled; } + + @Override + void storeInt(String key, int value) { + mStoredValues.put(key, value); + } + + @Override + int retrieveInt(String key, int defaultValue) { + return mStoredValues.getOrDefault(key, defaultValue); + } }; mLooper = new TestLooper(); @@ -316,6 +329,18 @@ public class AdvancedProtectionServiceTest { } @Test + public void testUsbDataProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.isUsbDataProtectionEnabled()); + } + + @Test + public void testSetUsbDataProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.setUsbDataProtectionEnabled(true)); + } + + @Test public void testRegisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback( diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java index 4a97b4670289..291d0ec8fbfc 100644 --- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java @@ -85,7 +85,7 @@ public class DiskStatsLoggingServiceTest extends AndroidTestCase { mDownloads = new TemporaryFolder(); mDownloads.create(); mStorageStats = new ExternalStorageStats(); - when(mSsm.queryExternalStatsForUser(isNull(String.class), any(UserHandle.class))) + when(mSsm.queryExternalStatsForUser((String)isNull(), any(UserHandle.class))) .thenReturn(mStorageStats); when(mJobService.getSystemService(anyString())).thenReturn(mSsm); } 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 fbf906586e8b..c59f0a05c619 100644 --- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt +++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt @@ -17,6 +17,7 @@ package com.android.server.supervision import android.app.Activity +import android.app.KeyguardManager import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManagerInternal import android.app.supervision.flags.Flags @@ -61,6 +62,9 @@ class SupervisionServiceTest { @get:Rule val mocks: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal + + @Mock + private lateinit var mockKeyguardManager: KeyguardManager @Mock private lateinit var mockPackageManager: PackageManager @Mock private lateinit var mockUserManagerInternal: UserManagerInternal @@ -71,7 +75,7 @@ class SupervisionServiceTest { @Before fun setUp() { context = InstrumentationRegistry.getInstrumentation().context - context = SupervisionContextWrapper(context, mockPackageManager) + context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager) LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java) LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal) @@ -250,11 +254,41 @@ class SupervisionServiceTest { @Test fun createConfirmSupervisionCredentialsIntent() { + service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true) + whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true) + val intent = checkNotNull(service.createConfirmSupervisionCredentialsIntent()) assertThat(intent.action).isEqualTo(ACTION_CONFIRM_SUPERVISION_CREDENTIALS) assertThat(intent.getPackage()).isEqualTo("com.android.settings") } + @Test + fun createConfirmSupervisionCredentialsIntent_supervisionNotEnabled_returnsNull() { + service.mInternal.setSupervisionEnabledForUser(context.getUserId(), false) + whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true) + + assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull() + } + + @Test + fun createConfirmSupervisionCredentialsIntent_noSupervisingUser_returnsNull() { + service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true) + whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(UserHandle.USER_NULL) + + assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull() + } + + @Test + fun createConfirmSupervisionCredentialsIntent_supervisingUserMissingSecureLock_returnsNull() { + service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true) + whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID) + whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false) + + assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull() + } + private val systemSupervisionPackage: String get() = context.getResources().getString(R.string.config_systemSupervision) @@ -279,6 +313,7 @@ class SupervisionServiceTest { private companion object { const val USER_ID = 100 const val APP_UID = USER_ID * UserHandle.PER_USER_RANGE + const val SUPERVISING_USER_ID = 10 } } @@ -286,10 +321,19 @@ class SupervisionServiceTest { * A context wrapper that allows broadcast intents to immediately invoke the receivers without * performing checks on the sending user. */ -private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) : - ContextWrapper(context) { +private class SupervisionContextWrapper( + val context: Context, + val keyguardManager: KeyguardManager, + val pkgManager: PackageManager, +) : ContextWrapper(context) { val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>() + override fun getSystemService(name: String): Any = + when (name) { + Context.KEYGUARD_SERVICE -> keyguardManager + else -> super.getSystemService(name) + } + override fun getPackageManager() = pkgManager override fun registerReceiverForAllUsers( diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java index f3d5e39ec127..8d164e1acf74 100644 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java @@ -53,14 +53,16 @@ import android.app.AppOpsManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.Bundle; +import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManagerInternal; +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.service.dreams.DreamManagerInternal; import android.testing.TestableContext; -import android.view.contentprotection.flags.Flags; +import android.view.KeyEvent; import androidx.test.filters.SmallTest; @@ -102,6 +104,8 @@ public class PhoneWindowManagerTests { public final TestableContext mContext = spy( new TestableContext(getInstrumentation().getContext())); + @Mock private IBinder mInputToken; + PhoneWindowManager mPhoneWindowManager; @Mock private ActivityTaskManagerInternal mAtmInternal; @@ -125,6 +129,8 @@ public class PhoneWindowManagerTests { @Mock private LockPatternUtils mLockPatternUtils; + private static final int INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY = 0; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -216,7 +222,7 @@ public class PhoneWindowManagerTests { @Test public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() { - mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); + mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); int[] outAppOp = new int[1]; assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER, /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY)); @@ -225,7 +231,7 @@ public class PhoneWindowManagerTests { @Test public void testCheckAddPermission_withAccessibilityOverlay() { - mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); + mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); int[] outAppOp = new int[1]; assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY, /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY)); @@ -234,7 +240,7 @@ public class PhoneWindowManagerTests { @Test public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() { - mSetFlagsRule.disableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); + mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED); int[] outAppOp = new int[1]; assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY, /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY)); @@ -401,6 +407,35 @@ public class PhoneWindowManagerTests { verify(mDreamManagerInternal).requestDream(); } + @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS) + public void testInterceptKeyBeforeDispatching() { + // Handle sub-tasks of init(). + doNothing().when(mPhoneWindowManager).updateSettings(any()); + doNothing().when(mPhoneWindowManager).initializeHdmiState(); + final DisplayPolicy displayPolicy = mock(DisplayPolicy.class); + mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy; + mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class); + final PowerManager pm = mock(PowerManager.class); + doReturn(true).when(pm).isInteractive(); + doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE)); + + mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init( + new PhoneWindowManager.Injector(mContext, + mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0); + + // Case: KeyNotConsumed with meta key. + KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_A, 0, KeyEvent.META_META_ON); + long result = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent, 0); + assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result); + + // Case: KeyNotConsumed without meta key. + KeyEvent keyEvent1 = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_ESCAPE, 0, 0); + long result1 = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent1, 0); + assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result1); + } + private void initPhoneWindowManager() { mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy; mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java index f76a9cdbb894..ba9bf1bf8045 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import android.platform.test.annotations.Presubmit; @@ -143,4 +144,46 @@ public class DeviceStateAutoRotateSettingIssueLoggerTests { anyInt(), anyBoolean()), never()); } + + @Test + public void onStateChange_issueOccurredSettingChangedTwice_reportOnlyOnce() { + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange(); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange(); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange(); + + verify(() -> + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED), + anyInt(), + anyBoolean()), times(1)); + } + + @Test + public void onStateChange_issueOccurredDeviceStateChangedTwice_reportOnlyOnce() { + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange(); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange(); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange(); + + verify(() -> + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED), + anyInt(), + anyBoolean()), times(1)); + } + + @Test + public void onStateChange_issueOccurredAfterDelay_reportOnce() { + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange(); + mTestTimeSupplier.delay( + DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS + DELAY); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange(); + mTestTimeSupplier.delay(DELAY); + mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange(); + + verify(() -> + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED), + eq(DELAY), + anyBoolean()), times(1)); + } } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index c244168c65fd..51ce144881b7 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -2912,38 +2912,48 @@ public final class Call { if (bundle.size() != newBundle.size()) { return false; } - - for(String key : bundle.keySet()) { - if (key != null) { - if (!newBundle.containsKey(key)) { - return false; - } - // In case new call extra contains non-framework class objects, return false to - // force update the call extra - try { - final Object value = bundle.get(key); - final Object newValue = newBundle.get(key); - if (value instanceof Bundle && newValue instanceof Bundle) { - if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) { - return false; - } + try { + for (String key : bundle.keySet()) { + if (key != null) { + if (!newBundle.containsKey(key)) { + return false; } - if (value instanceof byte[] && newValue instanceof byte[]) { - if (!Arrays.equals((byte[]) value, (byte[]) newValue)) { + // In case new call extra contains non-framework class objects, return false to + // force update the call extra + try { + final Object value = bundle.get(key); + final Object newValue = newBundle.get(key); + if (value instanceof Bundle && newValue instanceof Bundle) { + if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) { + return false; + } + } + if (value instanceof byte[] && newValue instanceof byte[]) { + if (!Arrays.equals((byte[]) value, (byte[]) newValue)) { + return false; + } + } else if (!Objects.equals(value, newValue)) { return false; } - } else if (!Objects.equals(value, newValue)) { + } catch (BadParcelableException e) { return false; } - } catch (BadParcelableException e) { - return false; - } catch (ClassCastException e) { - Log.e(LOG_TAG, e, "areBundlesEqual: failure comparing bundle key %s", key); - // until we know what is causing this, we should rethrow -- this is still not - // expected. - throw e; } } + } catch (ClassCastException | ArrayIndexOutOfBoundsException e) { + // Unfortunately this may get raised when accessing the bundle's keyset, so we cannot + // determine WHY a class cast exception is happening. We had tried in the past to do + // this down in the for loop so we could figure out which key is causing an issue. + // Bundles are not thread safe, so the most likely issue here is that the InCallService + // implementation is accessing the Bundle WHILE an incoming Telecom update comes in to + // potentially replace the Bundle. We call "areBundlesEqual" to see if the newly + // unparceled Call.Details is the same as what is already in the current Call instance. + // If those two operations overleave, I can see the potential for concurrent + // modification and edit of the Bundle. So we'll just catch here and assume the Bundles + // are not the same. This means a Call.CallBack may fire the onCallDetails changed + // callback when the Bundle didn't actually change. + Log.e(LOG_TAG, e, "areBundlesEqual: failed!"); + return false; } return true; } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 270d599e0a0a..6b4b0ee93684 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -700,6 +700,56 @@ public final class SatelliteManager { public @interface DisplayMode {} /** + * Unknown or unsupported value for data mode on satellite. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS) + public static final int SATELLITE_DATA_SUPPORT_UNKNOWN = -1; + + /** + * Support only restricted data usecases like carrier messaging using RCS. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS) + public static final int SATELLITE_DATA_SUPPORT_RESTRICTED = 0; + + /** + * Support constrained internet which would enable internet only for applications that are + * modified. + * + * <p> + * To get internet access, applications need to be modified to use the satellite data + * optimized network. This can be done by setting the {@link #PROPERTY_SATELLITE_DATA_OPTIMIZED} + * property to {@code true} in the manifest. + * </p> + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS) + public static final int SATELLITE_DATA_SUPPORT_CONSTRAINED = 1; + + /** + * Support default internet on satellite without any restrictions on any apps. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS) + public static final int SATELLITE_DATA_SUPPORT_UNCONSTRAINED = 2; + + /** @hide */ + @IntDef(prefix = {"SATELLITE_DATA_SUPPORT_"}, value = { + SATELLITE_DATA_SUPPORT_UNKNOWN, + SATELLITE_DATA_SUPPORT_RESTRICTED, + SATELLITE_DATA_SUPPORT_CONSTRAINED, + SATELLITE_DATA_SUPPORT_UNCONSTRAINED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SatelliteDataSupportMode {} + + /** * The emergency call is handed over to oem-enabled satellite SOS messaging. SOS messages are * sent to SOS providers, which will then forward the messages to emergency providers. * @hide @@ -3788,6 +3838,39 @@ public final class SatelliteManager { return appsNames; } + /** + * Method to return the current satellite data service policy supported mode for the + * subscriptionId based on carrier config. + * + * @param subId current subscription id. + * + * @return Supported modes {@link SatelliteDataSupportMode} + * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) + @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS) + @SatelliteDataSupportMode + public int getSatelliteDataSupportMode(int subId) { + int satelliteMode = SATELLITE_DATA_SUPPORT_UNKNOWN; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + satelliteMode = telephony.getSatelliteDataSupportMode(subId); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + loge("getSatelliteDataSupportMode() RemoteException:" + ex); + ex.rethrowAsRuntimeException(); + } + + return satelliteMode; + } + @Nullable private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b8aa9e8646bd..d7f80a94081a 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3641,4 +3641,19 @@ interface ITelephony { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") List<String> getSatelliteDataOptimizedApps(); + + /** + * Method to return the current satellite data service policy supported mode for the + * subscriptionId based on subscription id. Note: Iif any error or invalid sub id + * {@Link SatelliteDataSupportMode#SATELLITE_DATA_SUPPORT_UNKNOWN} will be returned. + * + * @param subId current subscription id. + * + * @return Supported modes {@link SatelliteDataSupportMode} + * @throws IllegalArgumentException if the subscription is invalid. + * @hide + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.SATELLITE_COMMUNICATION)") + int getSatelliteDataSupportMode(in int subId); } diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 7ec8f9ce9864..71c7a6b1119d 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -35,8 +35,8 @@ import android.os.PermissionEnforcer import android.os.SystemClock import android.os.test.FakePermissionEnforcer import android.os.test.TestLooper -import android.platform.test.annotations.Presubmit 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.view.View.OnKeyListener @@ -373,6 +373,29 @@ class InputManagerServiceTests { } @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS) + fun testInterceptKeyBeforeDispatchingWithFallthroughEvent() { + service.systemRunning() + overrideSendActionKeyEventsToFocusedWindow( + /* hasPermission = */false, + /* hasPrivateFlag = */false + ) + whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(0) + + // Create a fallback for a key event with a meta modifier. Should result in -2, + // which represents the fallback event, which indicates that original key event will + // be ignored (not sent to app) and instead the fallback will be created and sent to the + // app. + val fallbackAction: KeyCharacterMap.FallbackAction = KeyCharacterMap.FallbackAction.obtain() + fallbackAction.keyCode = KeyEvent.KEYCODE_SEARCH + whenever(kcm.getFallbackAction(anyInt(), anyInt())).thenReturn(fallbackAction) + + val event = KeyEvent( /* downTime= */0, /* eventTime= */0, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_SPACE, /* repeat= */0, KeyEvent.META_META_ON) + assertEquals(-2, service.interceptKeyBeforeDispatching(null, event, 0)) + } + + @Test fun testKeyEventsNotForwardedToFocusedWindow_whenWmConsumes() { service.systemRunning() overrideSendActionKeyEventsToFocusedWindow( diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java index adefac64dbae..6846d489ecaa 100644 --- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java @@ -231,7 +231,7 @@ public class SmsApplicationTest { .replacePreferredActivity(intentFilterCaptor.capture(), eq(IntentFilter.MATCH_CATEGORY_SCHEME | IntentFilter.MATCH_ADJUSTMENT_NORMAL), - isNotNull(List.class), + (List<ComponentName>)isNotNull(), eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME))); Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream() diff --git a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt index ae00df123353..4f7e8d13c4ba 100644 --- a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt +++ b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt @@ -18,21 +18,24 @@ package com.android.protolog.tool import java.lang.Exception -open class CodeProcessingException(message: String, context: ParsingContext) - : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" + - " $message") +open class CodeProcessingException( + message: String, context: ParsingContext, cause: Throwable? = null +) : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" + + " $message", cause) -class HashCollisionException(message: String, context: ParsingContext) : - CodeProcessingException(message, context) +class HashCollisionException( + message: String, context: ParsingContext, cause: Throwable? = null +) : CodeProcessingException(message, context, cause) -class IllegalImportException(message: String, context: ParsingContext) : - CodeProcessingException("Illegal import: $message", context) +class IllegalImportException(message: String, context: ParsingContext, cause: Throwable? = null) : + CodeProcessingException("Illegal import: $message", context, cause) -class InvalidProtoLogCallException(message: String, context: ParsingContext) - : CodeProcessingException("InvalidProtoLogCall: $message", context) +class InvalidProtoLogCallException( + message: String, context: ParsingContext, cause: Throwable? = null +) : CodeProcessingException("InvalidProtoLogCall: $message", context, cause) -class ParsingException(message: String, context: ParsingContext) - : CodeProcessingException(message, context) +class ParsingException(message: String, context: ParsingContext, cause: Throwable? = null) : + CodeProcessingException(message, context, cause) class InvalidViewerConfigException(message: String) : Exception(message) diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt index 272d8bb1793d..1f6db5fe2d37 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt @@ -118,9 +118,17 @@ class ProtoLogCallProcessorImpl( "- not a ProtoLogGroup enum member: $call", context) } - logCallVisitor?.processCall(call, messageString, getLevelForMethodName( - call.name.toString(), call, context), groupMap.getValue(groupName), - context.lineNumber) + try { + logCallVisitor?.processCall( + call, messageString, getLevelForMethodName( + call.name.toString(), call, context + ), groupMap.getValue(groupName), + context.lineNumber + ) + } catch (e: Throwable) { + throw InvalidProtoLogCallException("Error processing log call: $call", + context, e) + } } else if (call.name.id == "init") { // No processing } else { diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt index 004d97babbad..732824ae841c 100644 --- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt +++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt @@ -16,11 +16,14 @@ package com.android.protolog.tool +import com.android.internal.protolog.common.InvalidFormatStringException import com.android.internal.protolog.common.LogLevel import com.github.javaparser.StaticJavaParser import com.github.javaparser.ast.expr.MethodCallExpr import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows import org.junit.Test +import com.google.common.truth.Truth class ProtoLogCallProcessorImplTest { private data class LogCall( @@ -228,4 +231,38 @@ class ProtoLogCallProcessorImplTest { visitor.process(StaticJavaParser.parse(code), processor, "") checkCalls() } + + @Test + fun throws_clear_error_message_on_invalid_format_exception() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "Invalid message %9 %"); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager") + + val processor = object : ProtoLogCallVisitor { + override fun processCall( + call: MethodCallExpr, + messageString: String, + level: LogLevel, + group: LogGroup, + lineNumber: Int, + ) { + throw InvalidFormatStringException("Invalid Protolog message format") + } + } + + val exception = assertThrows(InvalidProtoLogCallException::class.java) { + visitor.process(StaticJavaParser.parse(code), processor, "MyTestFile.java") + } + Truth.assertThat(exception).hasMessageThat() + .contains("Code processing error in MyTestFile.java:6") + Truth.assertThat(exception.cause).hasMessageThat() + .contains("Invalid Protolog message format") + } } |