diff options
43 files changed, 921 insertions, 519 deletions
diff --git a/Ravenwood.bp b/Ravenwood.bp index 11da20aa6e02..159c17e13106 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -215,111 +215,37 @@ genrule { java_library { name: "services.core.ravenwood-jarjar", + defaults: ["ravenwood-internal-only-visibility-java"], installable: false, static_libs: [ "services.core.ravenwood", ], jarjar_rules: ":ravenwood-services-jarjar-rules", - visibility: ["//visibility:private"], -} - -java_library { - name: "services.fakes.ravenwood-jarjar", - installable: false, - srcs: [":services.fakes-sources"], - libs: [ - "ravenwood-framework", - "services.core.ravenwood", - ], - jarjar_rules: ":ravenwood-services-jarjar-rules", - visibility: ["//visibility:private"], -} - -java_library { - name: "mockito-ravenwood-prebuilt", - installable: false, - static_libs: [ - "mockito-robolectric-prebuilt", - ], -} - -java_library { - name: "inline-mockito-ravenwood-prebuilt", - installable: false, - static_libs: [ - "inline-mockito-robolectric-prebuilt", - ], } // Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically. // Rename some of the dependencies to make sure they're included in the intended order. java_genrule { name: "100-framework-minus-apex.ravenwood", + defaults: ["ravenwood-internal-only-visibility-genrule"], cmd: "cp $(in) $(out)", srcs: [":framework-minus-apex.ravenwood"], out: ["100-framework-minus-apex.ravenwood.jar"], - visibility: ["//visibility:private"], } java_genrule { // Use 200 to make sure it comes before the mainline stub ("all-updatable..."). name: "200-kxml2-android", + defaults: ["ravenwood-internal-only-visibility-genrule"], cmd: "cp $(in) $(out)", srcs: [":kxml2-android"], out: ["200-kxml2-android.jar"], - visibility: ["//visibility:private"], } java_genrule { name: "z00-all-updatable-modules-system-stubs", + defaults: ["ravenwood-internal-only-visibility-genrule"], cmd: "cp $(in) $(out)", srcs: [":all-updatable-modules-system-stubs"], out: ["z00-all-updatable-modules-system-stubs.jar"], - visibility: ["//visibility:private"], -} - -android_ravenwood_libgroup { - name: "ravenwood-runtime", - libs: [ - "100-framework-minus-apex.ravenwood", - "200-kxml2-android", - - "ravenwood-runtime-common-ravenwood", - - "android.test.mock.ravenwood", - "ravenwood-helper-runtime", - "hoststubgen-helper-runtime.ravenwood", - "services.core.ravenwood-jarjar", - "services.fakes.ravenwood-jarjar", - - // Provide runtime versions of utils linked in below - "junit", - "truth", - "flag-junit", - "ravenwood-framework", - "ravenwood-junit-impl", - "ravenwood-junit-impl-flag", - "mockito-ravenwood-prebuilt", - "inline-mockito-ravenwood-prebuilt", - - // It's a stub, so it should be towards the end. - "z00-all-updatable-modules-system-stubs", - ], - jni_libs: [ - "libandroid_runtime", - "libravenwood_runtime", - ], -} - -android_ravenwood_libgroup { - name: "ravenwood-utils", - libs: [ - "junit", - "truth", - "flag-junit", - "ravenwood-framework", - "ravenwood-junit", - "mockito-ravenwood-prebuilt", - "inline-mockito-ravenwood-prebuilt", - ], } diff --git a/core/java/android/util/SequenceUtils.java b/core/java/android/util/SequenceUtils.java new file mode 100644 index 000000000000..f833ce314c91 --- /dev/null +++ b/core/java/android/util/SequenceUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +/** + * Utilities to manage an info change seq id to ensure the update is in sync between client and + * system server. This should be used for info that can be updated though multiple IPC channel. + * + * To use it: + * 1. The system server should store the current seq as the source of truth, with initializing to + * {@link #getInitSeq}. + * 2. Whenever a newer info needs to be sent to the client side, the system server should first + * update its seq with {@link #getNextSeq}, then send the new info with the new seq to the client. + * 3. On the client side, when receiving a new info, it should only consume it if it is newer than + * the last received info seq by checking {@link #isIncomingSeqNewer}. + * + * @hide + */ +public final class SequenceUtils { + + private SequenceUtils() { + } + + /** + * Returns {@code true} if the incomingSeq is newer than the curSeq. + */ + public static boolean isIncomingSeqNewer(int curSeq, int incomingSeq) { + // Convert to long for comparison. + final long diff = (long) incomingSeq - curSeq; + // If there has been a sufficiently large jump, assume the sequence has wrapped around. + // For example, when the last seq is MAX_VALUE, the incoming seq will be MIN_VALUE + 1. + // diff = MIN_VALUE + 1 - MAX_VALUE. It is smaller than 0, but should be treated as newer. + return diff > 0 || diff < Integer.MIN_VALUE; + } + + /** Returns the initial seq. */ + public static int getInitSeq() { + return Integer.MIN_VALUE; + } + + /** Returns the next seq. */ + public static int getNextSeq(int seq) { + return seq == Integer.MAX_VALUE + // Skip the initial seq, so that when the app process is relaunched, the incoming + // seq from the server is always treated as newer. + ? getInitSeq() + 1 + : ++seq; + } +} diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 487214c5c33a..2efa647d3169 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -18,6 +18,7 @@ package android.view; import static android.graphics.PointProto.X; import static android.graphics.PointProto.Y; +import static android.util.SequenceUtils.getInitSeq; import static android.view.InsetsSourceControlProto.LEASH; import static android.view.InsetsSourceControlProto.POSITION; import static android.view.InsetsSourceControlProto.TYPE_NUMBER; @@ -266,6 +267,9 @@ public class InsetsSourceControl implements Parcelable { private @Nullable InsetsSourceControl[] mControls; + /** To make sure the info update between client and system server is in order. */ + private int mSeq = getInitSeq(); + public Array() { } @@ -280,9 +284,18 @@ public class InsetsSourceControl implements Parcelable { readFromParcel(in); } + public int getSeq() { + return mSeq; + } + + public void setSeq(int seq) { + mSeq = seq; + } + /** Updates the current Array to the given Array. */ public void setTo(@NonNull Array other, boolean copyControls) { set(other.mControls, copyControls); + mSeq = other.mSeq; } /** Updates the current controls to the given controls. */ @@ -336,11 +349,13 @@ public class InsetsSourceControl implements Parcelable { public void readFromParcel(Parcel in) { mControls = in.createTypedArray(InsetsSourceControl.CREATOR); + mSeq = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { out.writeTypedArray(mControls, flags); + out.writeInt(mSeq); } public static final @NonNull Creator<Array> CREATOR = new Creator<>() { @@ -362,6 +377,7 @@ public class InsetsSourceControl implements Parcelable { return false; } final InsetsSourceControl.Array other = (InsetsSourceControl.Array) o; + // mSeq is for internal bookkeeping only. return Arrays.equals(mControls, other.mControls); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 21eec67bfe10..bbd9acfd4cd7 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -17,6 +17,7 @@ package android.view; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.util.SequenceUtils.getInitSeq; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.InsetsStateProto.DISPLAY_CUTOUT; @@ -95,6 +96,9 @@ public class InsetsState implements Parcelable { /** The display shape */ private DisplayShape mDisplayShape = DisplayShape.NONE; + /** To make sure the info update between client and system server is in order. */ + private int mSeq = getInitSeq(); + public InsetsState() { mSources = new SparseArray<>(); } @@ -586,6 +590,14 @@ public class InsetsState implements Parcelable { } } + public int getSeq() { + return mSeq; + } + + public void setSeq(int seq) { + mSeq = seq; + } + public void set(InsetsState other) { set(other, false /* copySources */); } @@ -597,6 +609,7 @@ public class InsetsState implements Parcelable { mRoundedCornerFrame.set(other.mRoundedCornerFrame); mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds(); mDisplayShape = other.getDisplayShape(); + mSeq = other.mSeq; mSources.clear(); for (int i = 0, size = other.mSources.size(); i < size; i++) { final InsetsSource otherSource = other.mSources.valueAt(i); @@ -620,6 +633,7 @@ public class InsetsState implements Parcelable { mRoundedCornerFrame.set(other.mRoundedCornerFrame); mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds(); mDisplayShape = other.getDisplayShape(); + mSeq = other.mSeq; if (types == 0) { return; } @@ -705,6 +719,7 @@ public class InsetsState implements Parcelable { || !mRoundedCornerFrame.equals(state.mRoundedCornerFrame) || !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds) || !mDisplayShape.equals(state.mDisplayShape)) { + // mSeq is for internal bookkeeping only. return false; } @@ -778,6 +793,7 @@ public class InsetsState implements Parcelable { mRoundedCornerFrame.writeToParcel(dest, flags); dest.writeTypedObject(mPrivacyIndicatorBounds, flags); dest.writeTypedObject(mDisplayShape, flags); + dest.writeInt(mSeq); final int size = mSources.size(); dest.writeInt(size); for (int i = 0; i < size; i++) { @@ -803,6 +819,7 @@ public class InsetsState implements Parcelable { mRoundedCornerFrame.readFromParcel(in); mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR); mDisplayShape = in.readTypedObject(DisplayShape.CREATOR); + mSeq = in.readInt(); final int size = in.readInt(); final SparseArray<InsetsSource> sources; if (mSources == null) { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 405abd784bc9..f6535243eaac 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -4508,6 +4508,10 @@ public final class SurfaceControl implements Parcelable { Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled"); return this; } + if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { + SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( + "setFrameTimeline", this, null, "vsyncId=" + vsyncId); + } nativeSetFrameTimelineVsync(mNativeObject, vsyncId); return this; } @@ -4515,6 +4519,11 @@ public final class SurfaceControl implements Parcelable { /** @hide */ @NonNull public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) { + if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { + SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( + "setFrameTimelineVsync", this, null, "frameTimelineVsyncId=" + + frameTimelineVsyncId); + } nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId); return this; } diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java index d5398e6268dc..781a9019d1ae 100644 --- a/core/java/android/window/ClientWindowFrames.java +++ b/core/java/android/window/ClientWindowFrames.java @@ -16,6 +16,8 @@ package android.window; +import static android.util.SequenceUtils.getInitSeq; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; @@ -53,6 +55,9 @@ public class ClientWindowFrames implements Parcelable { public float compatScale = 1f; + /** To make sure the info update between client and system server is in order. */ + public int seq = getInitSeq(); + public ClientWindowFrames() { } @@ -74,6 +79,7 @@ public class ClientWindowFrames implements Parcelable { } isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout; compatScale = other.compatScale; + seq = other.seq; } /** Needed for AIDL out parameters. */ @@ -84,6 +90,7 @@ public class ClientWindowFrames implements Parcelable { attachedFrame = in.readTypedObject(Rect.CREATOR); isParentFrameClippedByDisplayCutout = in.readBoolean(); compatScale = in.readFloat(); + seq = in.readInt(); } @Override @@ -94,6 +101,7 @@ public class ClientWindowFrames implements Parcelable { dest.writeTypedObject(attachedFrame, flags); dest.writeBoolean(isParentFrameClippedByDisplayCutout); dest.writeFloat(compatScale); + dest.writeInt(seq); } @Override @@ -116,6 +124,7 @@ public class ClientWindowFrames implements Parcelable { return false; } final ClientWindowFrames other = (ClientWindowFrames) o; + // seq is for internal bookkeeping only. return frame.equals(other.frame) && displayFrame.equals(other.displayFrame) && parentFrame.equals(other.parentFrame) diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java index a7aef92b2f96..6b0ca9faa2a4 100644 --- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -181,6 +181,27 @@ public final class ShortcutUtils { } /** + * Converts {@link Settings.Secure} key to {@link UserShortcutType}. + * + * @param key The shortcut key in Settings. + * @return The mapped type + */ + @UserShortcutType + public static int convertToType(String key) { + return switch (key) { + case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE; + case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS; + case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE; + case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED -> + UserShortcutType.TRIPLETAP; + case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED -> + UserShortcutType.TWOFINGER_DOUBLETAP; + default -> throw new IllegalArgumentException( + "Unsupported user shortcut key: " + key); + }; + } + + /** * Updates an accessibility state if the accessibility service is a Always-On a11y service, * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON * <p> diff --git a/core/jni/Android.bp b/core/jni/Android.bp index e21d1dfac865..911bb19b0ddc 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -111,6 +111,7 @@ cc_library_shared_for_libandroid_runtime { "libminikin", "libz", "server_configurable_flags", + "libaconfig_storage_read_api_cc", "android.database.sqlite-aconfig-cc", "android.media.audiopolicy-aconfig-cc", ], diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java index 851e61259241..8cd6773936ef 100644 --- a/core/tests/coretests/src/android/os/MessageQueueTest.java +++ b/core/tests/coretests/src/android/os/MessageQueueTest.java @@ -16,7 +16,6 @@ package android.os; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.MediumTest; @@ -154,7 +153,6 @@ public class MessageQueueTest { @Test @MediumTest - @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFieldIntegrity() throws Exception { TestHandlerThread tester = new TestFieldIntegrityHandler() { diff --git a/core/tests/coretests/src/android/util/SequenceUtilsTest.java b/core/tests/coretests/src/android/util/SequenceUtilsTest.java new file mode 100644 index 000000000000..020520dbcf85 --- /dev/null +++ b/core/tests/coretests/src/android/util/SequenceUtilsTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + + +import static android.util.SequenceUtils.getInitSeq; +import static android.util.SequenceUtils.getNextSeq; +import static android.util.SequenceUtils.isIncomingSeqNewer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.annotations.Presubmit; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for subtypes of {@link SequenceUtils}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:SequenceUtilsTest + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +@DisabledOnRavenwood(blockedBy = SequenceUtils.class) +public class SequenceUtilsTest { + + // This is needed to disable the test in Ravenwood test, because SequenceUtils hasn't opted in + // for Ravenwood, which is still in experiment. + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test + public void testNextSeq() { + assertEquals(getInitSeq() + 1, getNextSeq(getInitSeq())); + assertEquals(getInitSeq() + 1, getNextSeq(Integer.MAX_VALUE)); + } + + @Test + public void testIsIncomingSeqNewer() { + assertTrue(isIncomingSeqNewer(getInitSeq() + 1, getInitSeq() + 10)); + assertFalse(isIncomingSeqNewer(getInitSeq() + 10, getInitSeq() + 1)); + assertTrue(isIncomingSeqNewer(-100, 100)); + assertFalse(isIncomingSeqNewer(100, -100)); + assertTrue(isIncomingSeqNewer(1, 2)); + assertFalse(isIncomingSeqNewer(2, 1)); + + // Possible incoming seq are all newer than the initial seq. + assertTrue(isIncomingSeqNewer(getInitSeq(), getInitSeq() + 1)); + assertTrue(isIncomingSeqNewer(getInitSeq(), -100)); + assertTrue(isIncomingSeqNewer(getInitSeq(), 0)); + assertTrue(isIncomingSeqNewer(getInitSeq(), 100)); + assertTrue(isIncomingSeqNewer(getInitSeq(), Integer.MAX_VALUE)); + assertTrue(isIncomingSeqNewer(getInitSeq(), getNextSeq(Integer.MAX_VALUE))); + + // False for the same seq. + assertFalse(isIncomingSeqNewer(getInitSeq(), getInitSeq())); + assertFalse(isIncomingSeqNewer(100, 100)); + assertFalse(isIncomingSeqNewer(Integer.MAX_VALUE, Integer.MAX_VALUE)); + + // True when there is a large jump (overflow). + assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 1)); + assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 100)); + assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getNextSeq(Integer.MAX_VALUE))); + } +} diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java index 1c72185ea93c..a8dce7032fa3 100644 --- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java +++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java @@ -17,7 +17,6 @@ package android.util; import static com.google.common.truth.Truth.assertThat; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; @@ -37,7 +36,6 @@ public class SparseSetArrayTest { public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test - @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testAddAll() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); @@ -59,7 +57,6 @@ public class SparseSetArrayTest { } @Test - @IgnoreUnderRavenwood(reason = "b/315036461") public void testCopyConstructor() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java index ac659e1bc593..6c6feaf4ae9c 100644 --- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java +++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java @@ -18,17 +18,19 @@ package android.util; import static org.junit.Assert.assertEquals; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.TimeZone; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @@ -42,6 +44,22 @@ public class TimeUtilsTest { public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + private TimeZone mOrigTimezone; + + @Before + public void setUp() { + mOrigTimezone = TimeZone.getDefault(); + + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + + @After + public void tearDown() { + if (mOrigTimezone != null) { + TimeZone.setDefault(mOrigTimezone); + } + } + @Test public void testFormatTime() { assertEquals("1672556400000 (now)", @@ -85,32 +103,29 @@ public class TimeUtilsTest { } @Test - @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testDumpTime() { - assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> { + assertEquals("2023-01-01 07:00:00.000", runWithPrintWriter((pw) -> { TimeUtils.dumpTime(pw, 1672556400000L); })); - assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> { + assertEquals("2023-01-01 07:00:00.000 (now)", runWithPrintWriter((pw) -> { TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L); })); - assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> { + assertEquals("2023-01-01 07:00:00.000 (-10ms)", runWithPrintWriter((pw) -> { TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10); })); } @Test - @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFormatForLogging() { assertEquals("unknown", TimeUtils.formatForLogging(0)); assertEquals("unknown", TimeUtils.formatForLogging(-1)); assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE)); - assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L)); + assertEquals("2023-01-01 07:00:00", TimeUtils.formatForLogging(1672556400000L)); } @Test - @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testLogTimeOfDay() { - assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L)); + assertEquals("01-01 07:00:00.000", TimeUtils.logTimeOfDay(1672556400000L)); } public static String runWithPrintWriter(Consumer<PrintWriter> consumer) { diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index 3256f31bdc93..5caedba364be 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -416,7 +416,8 @@ public class SurfaceTexture { } /** - * Retrieve the dataspace associated with the texture image. + * Retrieve the dataspace associated with the texture image + * set by the most recent call to {@link #updateTexImage}. */ @SuppressLint("MethodNameUnits") public @NamedDataSpace int getDataSpace() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 1279fc42c066..2aefc64a3ebb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -894,11 +894,22 @@ public class Bubble implements BubbleViewProvider { } @Nullable - Intent getAppBubbleIntent() { + @VisibleForTesting + public Intent getAppBubbleIntent() { return mAppIntent; } /** + * Sets the intent for a bubble that is an app bubble (one for which {@link #mIsAppBubble} is + * true). + * + * @param appIntent The intent to set for the app bubble. + */ + void setAppBubbleIntent(Intent appIntent) { + mAppIntent = appIntent; + } + + /** * Returns whether this bubble is from an app versus a notification. */ public boolean isAppBubble() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index d2c36e6b637c..c853301519e9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1450,6 +1450,8 @@ public class BubbleController implements ConfigurationChangeListener, if (b != null) { // It's in the overflow, so remove it & reinflate mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_NOTIF_CANCEL); + // Update the bubble entry in the overflow with the latest intent. + b.setAppBubbleIntent(intent); } else { // App bubble does not exist, lets add and expand it b = Bubble.createAppBubble(intent, user, icon, mMainExecutor); diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp index baafe7ba570c..a94c8c56a31f 100644 --- a/packages/CtsShim/Android.bp +++ b/packages/CtsShim/Android.bp @@ -61,7 +61,6 @@ android_app_import { "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_legacy", - "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_sdk_target_p", "com.android.apex.cts.shim.v3", ], @@ -102,7 +101,6 @@ android_app_import { "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_legacy", - "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_sdk_target_p", "com.android.apex.cts.shim.v3", ], diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index d6b7ecf5819d..5b3d47e9f74d 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -93,7 +93,6 @@ android_app { "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", - "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", "com.android.apex.cts.shim.v2_unsigned_payload", @@ -200,7 +199,6 @@ android_app { "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", - "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", "com.android.apex.cts.shim.v2_unsigned_payload", diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp index 2dbf2a212cc3..ac85d2b60327 100644 --- a/packages/CtsShim/build/jni/Android.bp +++ b/packages/CtsShim/build/jni/Android.bp @@ -33,7 +33,6 @@ cc_library_shared { "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", - "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", "com.android.apex.cts.shim.v2_unsigned_payload", diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index ec7150b5af2d..5242fe33a281 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.Idle @@ -450,4 +451,16 @@ class SceneInteractorTest : SysuiTestCase() { progress.value = 0.9f assertThat(transitionValue).isEqualTo(0f) } + + @Test + fun changeScene_toGone_whenKeyguardDisabled_doesNotThrow() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) + + underTest.changeScene(Scenes.Gone, "") + + assertThat(currentScene).isEqualTo(Scenes.Gone) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt index 8dede01cd20b..9cc0b3cd62e7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -48,6 +49,37 @@ constructor( transitionInteractor: KeyguardTransitionInteractor, ) { + /** + * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the + * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE. + * + * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold + * permission to do so (such as Phone). + * + * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in + * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were + * locked when it was disabled. + */ + val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled + + /** + * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it + * became disabled. + */ + val showKeyguardWhenReenabled: Flow<Boolean> = + repository.isKeyguardEnabled + // Whenever the keyguard is disabled... + .filter { enabled -> !enabled } + .sampleCombine( + transitionInteractor.currentTransitionInfoInternal, + biometricSettingsRepository.isCurrentUserInLockdown + ) + .map { (_, transitionInfo, inLockdown) -> + // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case, + // we want to remember that and re-show it when keyguard is enabled again. + transitionInfo.to != KeyguardState.GONE && !inLockdown + } + init { /** * Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already @@ -68,24 +100,6 @@ constructor( } } - /** - * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it - * became disabled. - */ - val showKeyguardWhenReenabled: Flow<Boolean> = - repository.isKeyguardEnabled - // Whenever the keyguard is disabled... - .filter { enabled -> !enabled } - .sampleCombine( - transitionInteractor.currentTransitionInfoInternal, - biometricSettingsRepository.isCurrentUserInLockdown - ) - .map { (_, transitionInfo, inLockdown) -> - // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case, - // we want to remember that and re-show it when keyguard is enabled again. - transitionInfo.to != KeyguardState.GONE && !inLockdown - } - fun notifyKeyguardEnabled(enabled: Boolean) { repository.setKeyguardEnabled(enabled) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 4738dbd2b21d..25a9e9e6f9f2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -22,6 +22,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.resolver.SceneResolver import com.android.systemui.scene.shared.logger.SceneLogger @@ -60,6 +61,7 @@ constructor( private val logger: SceneLogger, private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, + private val keyguardEnabledInteractor: KeyguardEnabledInteractor, ) { interface OnSceneAboutToChangeListener { @@ -381,7 +383,8 @@ constructor( val isChangeAllowed = to != Scenes.Gone || inMidTransitionFromGone || - deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked + deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked || + !keyguardEnabledInteractor.isKeyguardEnabled.value check(isChangeAllowed) { "Cannot change to the Gone scene while the device is locked and not currently" + " transitioning from Gone. Current transition state is ${transitionState.value}." + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 4c66f6617312..0bb18d704ac7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -58,7 +58,7 @@ class NotificationWakeUpCoordinator constructor( @Application applicationScope: CoroutineScope, dumpManager: DumpManager, - private val mHeadsUpManager: HeadsUpManager, + private val headsUpManager: HeadsUpManager, private val statusBarStateController: StatusBarStateController, private val bypassController: KeyguardBypassController, private val dozeParameters: DozeParameters, @@ -71,8 +71,8 @@ constructor( StatusBarStateController.StateListener, ShadeExpansionListener, Dumpable { - private lateinit var mStackScrollerController: NotificationStackScrollLayoutController - private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE + private lateinit var stackScrollerController: NotificationStackScrollLayoutController + private var visibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE private var inputLinearDozeAmount: Float = 0.0f private var inputEasedDozeAmount: Float = 0.0f @@ -85,13 +85,13 @@ constructor( private var outputEasedDozeAmount: Float = 0.0f @VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN - private var mNotificationVisibleAmount = 0.0f - private var mNotificationsVisible = false - private var mNotificationsVisibleForExpansion = false - private var mVisibilityAnimator: ObjectAnimator? = null - private var mVisibilityAmount = 0.0f - private var mLinearVisibilityAmount = 0.0f - private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>() + private var notificationVisibleAmount = 0.0f + private var notificationsVisible = false + private var notificationsVisibleForExpansion = false + private var visibilityAnimator: ObjectAnimator? = null + private var visibilityAmount = 0.0f + private var linearVisibilityAmount = 0.0f + private val entrySetToClearWhenFinished = mutableSetOf<NotificationEntry>() private var pulseExpanding: Boolean = false private val wakeUpListeners = arrayListOf<WakeUpListener>() private var state: Int = StatusBarState.KEYGUARD @@ -104,14 +104,14 @@ constructor( willWakeUp = false if (value) { if ( - mNotificationsVisible && - !mNotificationsVisibleForExpansion && + notificationsVisible && + !notificationsVisibleForExpansion && !bypassController.bypassEnabled ) { // We're waking up while pulsing, let's make sure the animation looks nice - mStackScrollerController.wakeUpFromPulse() + stackScrollerController.wakeUpFromPulse() } - if (bypassController.bypassEnabled && !mNotificationsVisible) { + if (bypassController.bypassEnabled && !notificationsVisible) { // Let's make sure our huns become visible once we are waking up in case // they were blocked by the proximity sensor updateNotificationVisibility( @@ -186,13 +186,13 @@ constructor( init { dumpManager.registerDumpable(this) - mHeadsUpManager.addListener(this) + headsUpManager.addListener(this) statusBarStateController.addCallback(this) bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener) addListener( object : WakeUpListener { override fun onFullyHiddenChanged(isFullyHidden: Boolean) { - if (isFullyHidden && mNotificationsVisibleForExpansion) { + if (isFullyHidden && notificationsVisibleForExpansion) { // When the notification becomes fully invisible, let's make sure our // expansion // flag also changes. This can happen if the bouncer shows when dragging @@ -217,7 +217,7 @@ constructor( } fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) { - mStackScrollerController = stackScrollerController + this.stackScrollerController = stackScrollerController pulseExpanding = stackScrollerController.isPulseExpanding stackScrollerController.setOnPulseHeightChangedListener { val nowExpanding = isPulseExpanding() @@ -237,7 +237,7 @@ constructor( } } - fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding + fun isPulseExpanding(): Boolean = stackScrollerController.isPulseExpanding /** * @param visible should notifications be visible @@ -249,13 +249,13 @@ constructor( animate: Boolean, increaseSpeed: Boolean ) { - mNotificationsVisibleForExpansion = visible + notificationsVisibleForExpansion = visible updateNotificationVisibility(animate, increaseSpeed) - if (!visible && mNotificationsVisible) { + if (!visible && notificationsVisible) { // If we stopped expanding and we're still visible because we had a pulse that hasn't // times out, let's release them all to make sure were not stuck in a state where // notifications are visible - mHeadsUpManager.releaseAllImmediately() + headsUpManager.releaseAllImmediately() } } @@ -269,12 +269,12 @@ constructor( private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore - var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications() + var visible = notificationsVisibleForExpansion || headsUpManager.hasNotifications() visible = visible && canShowPulsingHuns if ( !visible && - mNotificationsVisible && + notificationsVisible && (wakingUp || willWakeUp) && outputLinearDozeAmount != 0.0f ) { @@ -290,11 +290,11 @@ constructor( animate: Boolean, increaseSpeed: Boolean ) { - if (mNotificationsVisible == visible) { + if (notificationsVisible == visible) { return } - mNotificationsVisible = visible - mVisibilityAnimator?.cancel() + notificationsVisible = visible + visibilityAnimator?.cancel() if (animate) { notifyAnimationStart(visible) startVisibilityAnimation(increaseSpeed) @@ -371,7 +371,7 @@ constructor( state = statusBarStateController.state, changed = changed ) - mStackScrollerController.setDozeAmount(outputEasedDozeAmount) + stackScrollerController.setDozeAmount(outputEasedDozeAmount) updateHideAmount() if (changed && outputLinearDozeAmount == 0.0f) { setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) @@ -475,7 +475,7 @@ constructor( this.collapsedEnoughToHide = collapsedEnough if (couldShowPulsingHuns && !canShowPulsingHuns) { updateNotificationVisibility(animate = true, increaseSpeed = true) - mHeadsUpManager.releaseAllImmediately() + headsUpManager.releaseAllImmediately() } } } @@ -562,12 +562,12 @@ constructor( } private fun startVisibilityAnimation(increaseSpeed: Boolean) { - if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) { - mVisibilityInterpolator = - if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE + if (notificationVisibleAmount == 0f || notificationVisibleAmount == 1f) { + visibilityInterpolator = + if (notificationsVisible) Interpolators.TOUCH_RESPONSE else Interpolators.FAST_OUT_SLOW_IN_REVERSE } - val target = if (mNotificationsVisible) 1.0f else 0.0f + val target = if (notificationsVisible) 1.0f else 0.0f val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target) visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() @@ -576,34 +576,34 @@ constructor( } visibilityAnimator.duration = duration visibilityAnimator.start() - mVisibilityAnimator = visibilityAnimator + this.visibilityAnimator = visibilityAnimator } private fun setVisibilityAmount(visibilityAmount: Float) { logger.logSetVisibilityAmount(visibilityAmount) - mLinearVisibilityAmount = visibilityAmount - mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount) + linearVisibilityAmount = visibilityAmount + this.visibilityAmount = visibilityInterpolator.getInterpolation(visibilityAmount) handleAnimationFinished() updateHideAmount() } private fun handleAnimationFinished() { - if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) { - mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) } - mEntrySetToClearWhenFinished.clear() + if (outputLinearDozeAmount == 0.0f || linearVisibilityAmount == 0.0f) { + entrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) } + entrySetToClearWhenFinished.clear() } } private fun updateHideAmount() { - val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount) - val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount) + val linearAmount = min(1.0f - linearVisibilityAmount, outputLinearDozeAmount) + val amount = min(1.0f - visibilityAmount, outputEasedDozeAmount) logger.logSetHideAmount(linearAmount) - mStackScrollerController.setHideAmount(linearAmount, amount) + stackScrollerController.setHideAmount(linearAmount, amount) notificationsFullyHidden = linearAmount == 1.0f } private fun notifyAnimationStart(awake: Boolean) { - mStackScrollerController.notifyHideAnimationStart(!awake) + stackScrollerController.notifyHideAnimationStart(!awake) } override fun onDozingChanged(isDozing: Boolean) { @@ -615,7 +615,7 @@ constructor( override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { var animate = shouldAnimateVisibility() if (!isHeadsUp) { - if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) { + if (outputLinearDozeAmount != 0.0f && linearVisibilityAmount != 0.0f) { if (entry.isRowDismissed) { // if we animate, we see the shelf briefly visible. Instead we fully animate // the notification and its background out @@ -623,11 +623,11 @@ constructor( } else if (!wakingUp && !willWakeUp) { // TODO: look that this is done properly and not by anyone else entry.setHeadsUpAnimatingAway(true) - mEntrySetToClearWhenFinished.add(entry) + entrySetToClearWhenFinished.add(entry) } } - } else if (mEntrySetToClearWhenFinished.contains(entry)) { - mEntrySetToClearWhenFinished.remove(entry) + } else if (entrySetToClearWhenFinished.contains(entry)) { + entrySetToClearWhenFinished.remove(entry) entry.setHeadsUpAnimatingAway(false) } updateNotificationVisibility(animate, increaseSpeed = false) @@ -644,11 +644,11 @@ constructor( pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource") pw.println("outputLinearDozeAmount: $outputLinearDozeAmount") pw.println("outputEasedDozeAmount: $outputEasedDozeAmount") - pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount") - pw.println("mNotificationsVisible: $mNotificationsVisible") - pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion") - pw.println("mVisibilityAmount: $mVisibilityAmount") - pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount") + pw.println("notificationVisibleAmount: $notificationVisibleAmount") + pw.println("notificationsVisible: $notificationsVisible") + pw.println("notificationsVisibleForExpansion: $notificationsVisibleForExpansion") + pw.println("visibilityAmount: $visibilityAmount") + pw.println("linearVisibilityAmount: $linearVisibilityAmount") pw.println("pulseExpanding: $pulseExpanding") pw.println("state: ${StatusBarState.toString(state)}") pw.println("fullyAwake: $fullyAwake") @@ -698,7 +698,7 @@ constructor( } override fun get(coordinator: NotificationWakeUpCoordinator): Float { - return coordinator.mLinearVisibilityAmount + return coordinator.linearVisibilityAmount } } 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 fabb9b770a11..c5fbc39e79c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -25,6 +25,8 @@ import static android.service.notification.NotificationListenerService.NOTIFICAT import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static androidx.test.ext.truth.content.IntentSubject.assertThat; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING; import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR; @@ -2017,6 +2019,31 @@ public class BubblesTest extends SysuiTestCase { } @Test + public void testShowOrHideAppBubble_updateExistedBubbleInOverflow_updateIntentInBubble() { + String appBubbleKey = Bubble.getAppBubbleKeyForApp(mAppBubbleIntent.getPackage(), mUser0); + mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon); + // Collapse the stack so we don't need to wait for the dismiss animation in the test + mBubbleController.collapseStack(); + // Dismiss the app bubble so it's in the overflow + mBubbleController.dismissBubble(appBubbleKey, Bubbles.DISMISS_USER_GESTURE); + assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNotNull(); + + // Modify the intent to include new extras. + Intent newAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class) + .setPackage(mContext.getPackageName()) + .putExtra("hello", "world"); + + // Calling this while collapsed will re-add and expand the app bubble + mBubbleController.showOrHideAppBubble(newAppBubbleIntent, mUser0, mAppBubbleIcon); + assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey); + assertThat(mBubbleController.isStackExpanded()).isTrue(); + assertThat(mBubbleData.getBubbles().size()).isEqualTo(1); + assertThat(mBubbleData.getBubbles().get(0).getAppBubbleIntent()).extras().string( + "hello").isEqualTo("world"); + assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull(); + } + + @Test public void testCreateBubbleFromOngoingNotification() { NotificationEntry notif = new NotificationEntryBuilder() .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index 066736c1e036..0921eb9e83d3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.interactor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.data.repository.sceneContainerRepository @@ -31,5 +32,6 @@ val Kosmos.sceneInteractor by logger = sceneLogger, sceneFamilyResolvers = { sceneFamilyResolvers }, deviceUnlockedInteractor = deviceUnlockedInteractor, + keyguardEnabledInteractor = keyguardEnabledInteractor, ) } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 48bc803f4a5c..ad216b59c93d 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -276,3 +276,77 @@ sh_test_host { ":services.core.ravenwood.keep_all", ], } + +java_library { + name: "services.fakes.ravenwood-jarjar", + installable: false, + srcs: [":services.fakes-sources"], + libs: [ + "ravenwood-framework", + "services.core.ravenwood", + ], + jarjar_rules: ":ravenwood-services-jarjar-rules", + visibility: ["//visibility:private"], +} + +java_library { + name: "mockito-ravenwood-prebuilt", + installable: false, + static_libs: [ + "mockito-robolectric-prebuilt", + ], +} + +java_library { + name: "inline-mockito-ravenwood-prebuilt", + installable: false, + static_libs: [ + "inline-mockito-robolectric-prebuilt", + ], +} + +android_ravenwood_libgroup { + name: "ravenwood-runtime", + libs: [ + "100-framework-minus-apex.ravenwood", + "200-kxml2-android", + + "ravenwood-runtime-common-ravenwood", + + "android.test.mock.ravenwood", + "ravenwood-helper-runtime", + "hoststubgen-helper-runtime.ravenwood", + "services.core.ravenwood-jarjar", + "services.fakes.ravenwood-jarjar", + + // Provide runtime versions of utils linked in below + "junit", + "truth", + "flag-junit", + "ravenwood-framework", + "ravenwood-junit-impl", + "ravenwood-junit-impl-flag", + "mockito-ravenwood-prebuilt", + "inline-mockito-ravenwood-prebuilt", + + // It's a stub, so it should be towards the end. + "z00-all-updatable-modules-system-stubs", + ], + jni_libs: [ + "libandroid_runtime", + "libravenwood_runtime", + ], +} + +android_ravenwood_libgroup { + name: "ravenwood-utils", + libs: [ + "junit", + "truth", + "flag-junit", + "ravenwood-framework", + "ravenwood-junit", + "mockito-ravenwood-prebuilt", + "inline-mockito-ravenwood-prebuilt", + ], +} diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 93531508b3eb..b4efae3a05e4 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -11,6 +11,16 @@ flag { } flag { + name: "always_allow_observing_touch_events" + namespace: "accessibility" + description: "Always allows InputFilter observing SOURCE_TOUCHSCREEN events, even if touch exploration is enabled." + bug: "344604959" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "resettable_dynamic_properties" namespace: "accessibility" description: "Maintains initial copies of a11yServiceInfo dynamic properties so they can reset on disconnect." diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 5fb60e75cf85..f9196f3e0e0e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -1087,7 +1087,15 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } - private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { + private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { + if (Flags.alwaysAllowObservingTouchEvents()) { + final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); + if (isTouchEvent && !canShareGenericTouchEvent()) { + return false; + } + final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; + return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; + } // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing // touch exploration. if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) @@ -1095,13 +1103,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo return false; } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources - & mCombinedMotionEventObservedSources - & eventSourceWithoutClass) - != 0; + return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; } - private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { + private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { + if (Flags.alwaysAllowObservingTouchEvents()) { + final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; + return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; + } // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing // touch exploration. if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) @@ -1109,7 +1118,22 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo return false; } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; + return (mCombinedGenericMotionEventSources + & mCombinedMotionEventObservedSources + & eventSourceWithoutClass) + != 0; + } + + private boolean canShareGenericTouchEvent() { + if ((mCombinedMotionEventObservedSources & InputDevice.SOURCE_TOUCHSCREEN) != 0) { + // Share touch events if a MotionEvent-observing service wants them. + return true; + } + if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) == 0) { + // Share touch events if touch exploration is not enabled. + return true; + } + return false; } public void setCombinedGenericMotionEventSources(int sources) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4f9db8bece63..acd80eeac558 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -47,6 +47,11 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP; import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated; import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -923,25 +928,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub newValue, restoredFromSdk); } } - case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> { - synchronized (mLock) { - restoreAccessibilityButtonTargetsLocked( - previousValue, newValue); - } - } - case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> { - if (!android.view.accessibility.Flags.a11yQsShortcut()) { - return; - } - restoreAccessibilityQsTargets(newValue); - } - case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> { - if (!android.view.accessibility.Flags - .restoreA11yShortcutTargetService()) { - return; - } - restoreAccessibilityShortcutTargetService(previousValue, newValue); - } + case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + Settings.Secure.ACCESSIBILITY_QS_TARGETS, + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> + restoreShortcutTargets(newValue, + ShortcutUtils.convertToType(which)); } } } @@ -1040,7 +1031,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, targetsFromSetting, str -> str); - readAccessibilityButtonTargetsLocked(userState); + readAccessibilityShortcutTargetsLocked(userState, SOFTWARE); onUserStateChangedLocked(userState); } @@ -1720,12 +1711,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Turn on/off a11y qs shortcut for the a11y features based on the change in QS Panel if (!a11yFeaturesToEnable.isEmpty()) { - enableShortcutForTargets(/* enable= */ true, UserShortcutType.QUICK_SETTINGS, + enableShortcutForTargets(/* enable= */ true, QUICK_SETTINGS, a11yFeaturesToEnable, userId); } if (!a11yFeaturesToRemove.isEmpty()) { - enableShortcutForTargets(/* enable= */ false, UserShortcutType.QUICK_SETTINGS, + enableShortcutForTargets(/* enable= */ false, QUICK_SETTINGS, a11yFeaturesToRemove, userId); } } @@ -2057,100 +2048,78 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** - * User could enable accessibility services and configure accessibility button during the SUW. - * Merges current value of accessibility button settings into the restored one to make sure - * user's preferences of accessibility button updated in SUW are not lost. - * - * Called only during settings restore; currently supports only the owner user - * TODO: http://b/22388012 - */ - void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) { - final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting, - /* doMerge = */false); - readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting, - /* doMerge = */true); - - final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); - userState.mAccessibilityButtonTargets.clear(); - userState.mAccessibilityButtonTargets.addAll(targetsFromSetting); - persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str); - - scheduleNotifyClientsOfServicesStateChangeLocked(userState); - onUserStateChangedLocked(userState); - } - - /** * User could configure accessibility shortcut during the SUW before restoring user data. * Merges the current value and the new value to make sure we don't lost the setting the user's - * preferences of accessibility qs shortcut updated in SUW are not lost. - * - * Called only during settings restore; currently supports only the owner user + * preferences of accessibility shortcut updated in SUW are not lost. + * Called only during settings restore; currently supports only the owner user. + * <P> + * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}. + * </P> * TODO: http://b/22388012 */ - private void restoreAccessibilityQsTargets(String newValue) { + private void restoreShortcutTargets(String newValue, + @UserShortcutType int shortcutType) { + assertNoTapShortcut(shortcutType); + if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) { + return; + } + if (shortcutType == HARDWARE + && !android.view.accessibility.Flags.restoreA11yShortcutTargetService()) { + return; + } + synchronized (mLock) { final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); - final Set<String> mergedTargets = userState.getA11yQsTargets(); - readColonDelimitedStringToSet(newValue, str -> str, mergedTargets, - /* doMerge = */ true); + final Set<String> mergedTargets = (shortcutType == HARDWARE) + ? new ArraySet<>(ShortcutUtils.getShortcutTargetsFromSettings( + mContext, shortcutType, userState.mUserId)) + : userState.getShortcutTargetsLocked(shortcutType); + + // If dealing with the hardware shortcut, + // remove the default service if it wasn't present before restore, + // but only if the raw shortcut setting is not null (edge case during SUW). + // Otherwise, merge the old and new targets normally. + if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore() + && shortcutType == HARDWARE) { + final String defaultService = + mContext.getString(R.string.config_defaultAccessibilityService); + final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService) + ? null : ComponentName.unflattenFromString(defaultService); + boolean shouldClearDefaultService = defaultServiceComponent != null + && !stringSetContainsComponentName(mergedTargets, defaultServiceComponent); + readColonDelimitedStringToSet(newValue, str -> str, + mergedTargets, /*doMerge=*/true); + + if (shouldClearDefaultService && stringSetContainsComponentName( + mergedTargets, defaultServiceComponent)) { + Slog.i(LOG_TAG, "Removing default service " + defaultService + + " from restore of " + + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + mergedTargets.removeIf(str -> + defaultServiceComponent.equals(ComponentName.unflattenFromString(str))); + } + if (mergedTargets.isEmpty()) { + return; + } + } else { + readColonDelimitedStringToSet(newValue, str -> str, mergedTargets, + /* doMerge = */ true); + } - userState.updateA11yQsTargetLocked(mergedTargets); - persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + userState.updateShortcutTargetsLocked(mergedTargets, shortcutType); + persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType), UserHandle.USER_SYSTEM, mergedTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); onUserStateChangedLocked(userState); } } - /** - * Merges the old and restored value of - * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}. - * - * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from - * the merged set if it was not present before restoring. - */ - private void restoreAccessibilityShortcutTargetService( - String oldValue, String restoredValue) { - final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedStringToSet(oldValue, str -> str, - targetsFromSetting, /*doMerge=*/false); - final String defaultService = - mContext.getString(R.string.config_defaultAccessibilityService); - final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService) - ? null : ComponentName.unflattenFromString(defaultService); - boolean shouldClearDefaultService = defaultServiceComponent != null - && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent); - readColonDelimitedStringToSet(restoredValue, str -> str, - targetsFromSetting, /*doMerge=*/true); - if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) { - if (shouldClearDefaultService && stringSetContainsComponentName( - targetsFromSetting, defaultServiceComponent)) { - Slog.i(LOG_TAG, "Removing default service " + defaultService - + " from restore of " - + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); - targetsFromSetting.removeIf(str -> - defaultServiceComponent.equals(ComponentName.unflattenFromString(str))); - } - if (targetsFromSetting.isEmpty()) { - return; - } - } - synchronized (mLock) { - final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); - final Set<String> shortcutTargets = - userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE); - shortcutTargets.clear(); - shortcutTargets.addAll(targetsFromSetting); - persistColonDelimitedSetToSettingLocked( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - UserHandle.USER_SYSTEM, targetsFromSetting, str -> str); - scheduleNotifyClientsOfServicesStateChangeLocked(userState); - onUserStateChangedLocked(userState); - } + private String getRawShortcutSetting(int userId, @UserShortcutType int shortcutType) { + return Settings.Secure.getStringForUser(mContext.getContentResolver(), + ShortcutUtils.convertToKey(shortcutType), userId); } + /** * Returns {@code true} if the set contains the provided non-null {@link ComponentName}. * @@ -2263,7 +2232,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void showAccessibilityTargetsSelection(int displayId, @UserShortcutType int shortcutType) { final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); - final String chooserClassName = (shortcutType == UserShortcutType.HARDWARE) + final String chooserClassName = (shortcutType == HARDWARE) ? AccessibilityShortcutChooserActivity.class.getName() : AccessibilityButtonChooserActivity.class.getName(); intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); @@ -3236,9 +3205,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState); somethingChanged |= readMagnificationEnabledSettingsLocked(userState); somethingChanged |= readAutoclickEnabledSettingLocked(userState); - somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState); - somethingChanged |= readAccessibilityQsTargetsLocked(userState); - somethingChanged |= readAccessibilityButtonTargetsLocked(userState); + somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE); + somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS); + somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState); @@ -3386,60 +3355,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.setSendMotionEventsEnabled(sendMotionEvents); } - private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) { - final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId); + /** + * Throws an exception for {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} types. + */ + private boolean readAccessibilityShortcutTargetsLocked(AccessibilityUserState userState, + @UserShortcutType int shortcutType) { + assertNoTapShortcut(shortcutType); + final String settingValue = getRawShortcutSetting(userState.mUserId, shortcutType); final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false); - // Fall back to device's default a11y service, only when setting is never updated. - if (settingValue == null) { + // If dealing with an empty hardware shortcut, fall back to the default value. + if (shortcutType == HARDWARE && settingValue == null) { final String defaultService = mContext.getString( R.string.config_defaultAccessibilityService); if (!TextUtils.isEmpty(defaultService)) { - targetsFromSetting.add(defaultService); + // Convert to component name to reformat the target if it has a relative path. + ComponentName name = ComponentName.unflattenFromString(defaultService); + if (name != null) { + targetsFromSetting.add(name.flattenToString()); + } } + } else { + readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false); } - final Set<String> currentTargets = - userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE); - if (targetsFromSetting.equals(currentTargets)) { - return false; - } - currentTargets.clear(); - currentTargets.addAll(targetsFromSetting); - scheduleNotifyClientsOfServicesStateChangeLocked(userState); - return true; - } - - private boolean readAccessibilityQsTargetsLocked(AccessibilityUserState userState) { - final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_QS_TARGETS, - userState.mUserId, str -> str, targetsFromSetting); - - final Set<String> currentTargets = - userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS); - if (targetsFromSetting.equals(currentTargets)) { - return false; - } - userState.updateA11yQsTargetLocked(targetsFromSetting); - scheduleNotifyClientsOfServicesStateChangeLocked(userState); - return true; - } - - private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { - final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - userState.mUserId, str -> str, targetsFromSetting); - - final Set<String> currentTargets = - userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE); - if (targetsFromSetting.equals(currentTargets)) { - return false; + if (userState.updateShortcutTargetsLocked(targetsFromSetting, shortcutType)) { + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + return true; } - currentTargets.clear(); - currentTargets.addAll(targetsFromSetting); - scheduleNotifyClientsOfServicesStateChangeLocked(userState); - return true; + return false; } private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) { @@ -3487,14 +3430,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) { final Set<String> currentTargets = - userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE); - final int lastSize = currentTargets.size(); - if (lastSize == 0) { - return; - } + userState.getShortcutTargetsLocked(HARDWARE); currentTargets.removeIf( name -> !userState.isShortcutTargetInstalledLocked(name)); - if (lastSize == currentTargets.size()) { + if (!userState.updateShortcutTargetsLocked(currentTargets, HARDWARE)) { return; } @@ -3680,13 +3619,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final Set<String> currentTargets = userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE); - final int lastSize = currentTargets.size(); - if (lastSize == 0) { - return; - } currentTargets.removeIf( name -> !userState.isShortcutTargetInstalledLocked(name)); - if (lastSize == currentTargets.size()) { + if (!userState.updateShortcutTargetsLocked(currentTargets, SOFTWARE)) { return; } @@ -3719,8 +3654,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } final Set<String> buttonTargets = - userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE); - int lastSize = buttonTargets.size(); + userState.getShortcutTargetsLocked(SOFTWARE); buttonTargets.removeIf(name -> { if (packageName != null && name != null && !name.contains(packageName)) { return false; @@ -3752,13 +3686,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } return false; }); - boolean changed = (lastSize != buttonTargets.size()); - lastSize = buttonTargets.size(); final Set<String> shortcutKeyTargets = - userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE); + userState.getShortcutTargetsLocked(HARDWARE); final Set<String> qsShortcutTargets = - userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS); + userState.getShortcutTargetsLocked(QUICK_SETTINGS); userState.mEnabledServices.forEach(componentName -> { if (packageName != null && componentName != null && !packageName.equals(componentName.getPackageName())) { @@ -3790,8 +3722,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + " should be assign to the button or shortcut."); buttonTargets.add(serviceName); }); - changed |= (lastSize != buttonTargets.size()); - if (!changed) { + if (!userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE)) { return; } @@ -3815,10 +3746,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } final Set<String> targets = - userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS); + userState.getShortcutTargetsLocked(QUICK_SETTINGS); // Removes the targets that are no longer installed on the device. - boolean somethingChanged = targets.removeIf( + targets.removeIf( name -> !userState.isShortcutTargetInstalledLocked(name)); // Add the target if the a11y service is enabled and the tile exist in QS panel Set<ComponentName> enabledServices = userState.getEnabledServicesLocked(); @@ -3829,14 +3760,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub ComponentName tileService = a11yFeatureToTileService.getOrDefault(enabledService, null); if (tileService != null && currentA11yTilesInQsPanel.contains(tileService)) { - somethingChanged |= targets.add(enabledService.flattenToString()); + targets.add(enabledService.flattenToString()); } } - if (!somethingChanged) { + if (!userState.updateShortcutTargetsLocked(targets, QUICK_SETTINGS)) { return; } - userState.updateA11yQsTargetLocked(targets); // Update setting key with new value. persistColonDelimitedSetToSettingLocked( @@ -3862,14 +3792,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3); shortcutTypeAndShortcutSetting.add( - new Pair<>(UserShortcutType.HARDWARE, + new Pair<>(HARDWARE, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); shortcutTypeAndShortcutSetting.add( new Pair<>(UserShortcutType.SOFTWARE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)); if (android.view.accessibility.Flags.a11yQsShortcut()) { shortcutTypeAndShortcutSetting.add( - new Pair<>(UserShortcutType.QUICK_SETTINGS, + new Pair<>(QUICK_SETTINGS, Settings.Secure.ACCESSIBILITY_QS_TARGETS)); } @@ -3883,7 +3813,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub shortcutSettingName, userState.mUserId, currentTargets, str -> str); - if (shortcutType != UserShortcutType.QUICK_SETTINGS) { + if (shortcutType != QUICK_SETTINGS) { continue; } @@ -3968,7 +3898,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, - Display.DEFAULT_DISPLAY, UserShortcutType.HARDWARE, targetName)); + Display.DEFAULT_DISPLAY, HARDWARE, targetName)); } /** @@ -4115,7 +4045,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final boolean requestA11yButton = (installedServiceInfo.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; // Turns on / off the accessibility service - if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == UserShortcutType.HARDWARE) + if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == HARDWARE) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { if (serviceConnection == null) { logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, @@ -4129,7 +4059,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } return true; } - if (shortcutType == UserShortcutType.HARDWARE && targetSdk > Build.VERSION_CODES.Q + if (shortcutType == HARDWARE && targetSdk > Build.VERSION_CODES.Q && requestA11yButton) { if (!userState.getEnabledServicesLocked().contains(assignedTarget)) { enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); @@ -4222,7 +4152,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub validNewTargets = newTargets; // filter out targets that doesn't have qs shortcut - if (shortcutType == UserShortcutType.QUICK_SETTINGS) { + if (shortcutType == QUICK_SETTINGS) { validNewTargets = newTargets.stream().filter(target -> { ComponentName targetComponent = ComponentName.unflattenFromString(target); return featureToTileMap.containsKey(targetComponent); @@ -4240,10 +4170,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /* defaultEmptyString= */ "" ); - if (shortcutType == UserShortcutType.QUICK_SETTINGS) { + if (shortcutType == QUICK_SETTINGS) { int numOfFeatureChanged = Math.abs(currentTargets.size() - validNewTargets.size()); logMetricForQsShortcutConfiguration(enable, numOfFeatureChanged); - userState.updateA11yQsTargetLocked(validNewTargets); + userState.updateShortcutTargetsLocked(validNewTargets, QUICK_SETTINGS); scheduleNotifyClientsOfServicesStateChangeLocked(userState); onUserStateChangedLocked(userState); } @@ -4257,7 +4187,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Add or Remove tile in QS Panel - if (shortcutType == UserShortcutType.QUICK_SETTINGS) { + if (shortcutType == QUICK_SETTINGS) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::updateA11yTileServicesInQuickSettingsPanel, this, validNewTargets, currentTargets, userId)); @@ -4266,7 +4196,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!enable) { return; } - if (shortcutType == UserShortcutType.HARDWARE) { + if (shortcutType == HARDWARE) { skipVolumeShortcutDialogTimeoutRestriction(userId); if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) { persistIntToSetting( @@ -4461,6 +4391,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub shortcutTargets.add(serviceName); } } + userState.updateShortcutTargetsLocked(Set.copyOf(shortcutTargets), shortcutType); return shortcutTargets; } } @@ -5672,7 +5603,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || mShowImeWithHardKeyboardUri.equals(uri)) { userState.reconcileSoftKeyboardModeWithSettingsLocked(); } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) { - if (readAccessibilityShortcutKeySettingLocked(userState)) { + if (readAccessibilityShortcutTargetsLocked(userState, HARDWARE)) { onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonComponentIdUri.equals(uri)) { @@ -5680,7 +5611,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub onUserStateChangedLocked(userState); } } else if (mAccessibilityButtonTargetsUri.equals(uri)) { - if (readAccessibilityButtonTargetsLocked(userState)) { + if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) { onUserStateChangedLocked(userState); } } else if (mUserNonInteractiveUiTimeoutUri.equals(uri) @@ -6505,4 +6436,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub String metricId = enable ? METRIC_ID_QS_SHORTCUT_ADD : METRIC_ID_QS_SHORTCUT_REMOVE; Counter.logIncrementWithUid(metricId, Binder.getCallingUid(), numOfFeatures); } + + private void assertNoTapShortcut(@UserShortcutType int shortcutType) { + if ((shortcutType & (TRIPLETAP | TWOFINGER_DOUBLETAP)) != 0) { + throw new IllegalArgumentException("Tap shortcuts are not supported."); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index a37a1841fcc0..de1c86a1ac06 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -777,12 +777,15 @@ class AccessibilityUserState { * @return The array set of the strings */ public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) { + return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType)); + } + private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) { if (shortcutType == UserShortcutType.HARDWARE) { return mAccessibilityShortcutKeyTargets; } else if (shortcutType == UserShortcutType.SOFTWARE) { return mAccessibilityButtonTargets; } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) { - return getA11yQsTargets(); + return mAccessibilityQsTargets; } else if ((shortcutType == UserShortcutType.TRIPLETAP && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP @@ -795,6 +798,32 @@ class AccessibilityUserState { } /** + * Updates the corresponding shortcut targets with the provided set. + * Tap shortcuts don't operate using sets of targets, + * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} + * will instead throw an {@code IllegalArgumentException} + * @param newTargets set of targets to replace the existing set. + * @param shortcutType type to be replaced. + * @return {@code true} if the set was changed, or {@code false} if the elements are the same. + * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used. + */ + boolean updateShortcutTargetsLocked( + Set<String> newTargets, @UserShortcutType int shortcutType) { + final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP; + if ((shortcutType & mask) != 0) { + throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets."); + } + + final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType); + if (newTargets.equals(currentTargets)) { + return false; + } + currentTargets.clear(); + currentTargets.addAll(newTargets); + return true; + } + + /** * Whether or not the given shortcut target is installed in device. * * @param name The shortcut target name @@ -844,8 +873,9 @@ class AccessibilityUserState { ); } - Set<String> targets = getShortcutTargetsLocked(shortcutType); - boolean result = targets.removeIf(name -> { + // getting internal set lets us directly modify targets, as it's not a copy. + Set<String> targets = getShortcutTargetsInternalLocked(shortcutType); + return targets.removeIf(name -> { ComponentName componentName; if (name == null || (componentName = ComponentName.unflattenFromString(name)) == null) { @@ -853,11 +883,6 @@ class AccessibilityUserState { } return componentName.equals(target); }); - if (shortcutType == UserShortcutType.QUICK_SETTINGS) { - updateA11yQsTargetLocked(targets); - } - - return result; } /** @@ -1114,11 +1139,6 @@ class AccessibilityUserState { ); } - public void updateA11yQsTargetLocked(Set<String> targets) { - mAccessibilityQsTargets.clear(); - mAccessibilityQsTargets.addAll(targets); - } - /** * Returns a copy of the targets which has qs shortcut turned on */ diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index 363a4a7be3db..91a4d6f1707f 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -1126,7 +1126,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } } - private void sendHostEndpointConnectedEvent() { + void sendHostEndpointConnectedEvent() { HostEndpointInfo info = new HostEndpointInfo(); info.hostEndpointId = (char) mHostEndPointId; info.packageName = mPackage; diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index b3fb147a318b..7a722bc914f7 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -74,6 +74,7 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -158,10 +159,8 @@ public class ContextHubService extends IContextHubService.Stub { // A queue of reliable message records for duplicate detection private final PriorityQueue<ReliableMessageRecord> mReliableMessageRecordQueue = - new PriorityQueue<ReliableMessageRecord>( - (ReliableMessageRecord left, ReliableMessageRecord right) -> { - return Long.compare(left.getTimestamp(), right.getTimestamp()); - }); + new PriorityQueue<>( + Comparator.comparingLong(ReliableMessageRecord::getTimestamp)); // The test mode manager that manages behaviors during test mode. private final TestModeManager mTestModeManager = new TestModeManager(); @@ -179,10 +178,10 @@ public class ContextHubService extends IContextHubService.Stub { private boolean mIsBtMainEnabled = false; // True if test mode is enabled for the Context Hub - private AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false); + private final AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false); // A hashmap used to record if a contexthub is waiting for daily query - private Set<Integer> mMetricQueryPendingContextHubIds = + private final Set<Integer> mMetricQueryPendingContextHubIds = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>()); // Lock object for sendWifiSettingUpdate() @@ -242,10 +241,14 @@ public class ContextHubService extends IContextHubService.Stub { @Override public void handleServiceRestart() { - Log.i(TAG, "Starting Context Hub Service restart"); + Log.i(TAG, "Recovering from Context Hub HAL restart..."); initExistingCallbacks(); resetSettings(); - Log.i(TAG, "Finished Context Hub Service restart"); + if (Flags.reconnectHostEndpointsAfterHalRestart()) { + mClientManager.forEachClientOfHub(mContextHubId, + ContextHubClientBroker::sendHostEndpointConnectedEvent); + } + Log.i(TAG, "Finished recovering from Context Hub HAL restart"); } @Override @@ -317,11 +320,11 @@ public class ContextHubService extends IContextHubService.Stub { */ private static final int MAX_PROBABILITY_PERCENT = 100; - private Random mRandom = new Random(); + private final Random mRandom = new Random(); /** - * @see ContextHubServiceCallback.handleNanoappMessage * @return whether the message was handled + * @see ContextHubServiceCallback#handleNanoappMessage */ public boolean handleNanoappMessage(int contextHubId, short hostEndpointId, NanoAppMessage message, @@ -331,7 +334,8 @@ public class ContextHubService extends IContextHubService.Stub { } if (Flags.reliableMessageDuplicateDetectionService() - && didEventHappen(MESSAGE_DUPLICATION_PROBABILITY_PERCENT)) { + && mRandom.nextInt(MAX_PROBABILITY_PERCENT) + < MESSAGE_DUPLICATION_PROBABILITY_PERCENT) { Log.i(TAG, "[TEST MODE] Duplicating message (" + NUM_MESSAGES_TO_DUPLICATE + " sends) with message sequence number: " @@ -344,16 +348,6 @@ public class ContextHubService extends IContextHubService.Stub { } return false; } - - /** - * Returns true if the event with percentPercent did happen. - * - * @param probabilityPercent the percent probability of the event. - * @return true if the event happened, false otherwise. - */ - private boolean didEventHappen(int probabilityPercent) { - return mRandom.nextInt(MAX_PROBABILITY_PERCENT) < probabilityPercent; - } } public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) { @@ -476,7 +470,7 @@ public class ContextHubService extends IContextHubService.Stub { hubInfo = mContextHubWrapper.getHubs(); } catch (RemoteException e) { Log.e(TAG, "RemoteException while getting Context Hub info", e); - hubInfo = new Pair(Collections.emptyList(), Collections.emptyList()); + hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList()); } long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs; @@ -536,6 +530,7 @@ public class ContextHubService extends IContextHubService.Stub { for (int contextHubId : mContextHubIdToInfoMap.keySet()) { try { mContextHubWrapper.registerExistingCallback(contextHubId); + Log.i(TAG, "Re-registered callback to context hub " + contextHubId); } catch (RemoteException e) { Log.e(TAG, "RemoteException while registering existing service callback for hub " + "(ID = " + contextHubId + ")", e); @@ -647,7 +642,7 @@ public class ContextHubService extends IContextHubService.Stub { mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers( SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> { // If we are in HSUM mode, any user can change the microphone setting - if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) { + if (UserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) { Log.d(TAG, "User: " + userId + " mic privacy: " + enabled); sendMicrophoneDisableSettingUpdate(enabled); } @@ -720,33 +715,30 @@ public class ContextHubService extends IContextHubService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public int[] getContextHubHandles() throws RemoteException { + public int[] getContextHubHandles() { super.getContextHubHandles_enforcePermission(); - return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet()); } @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException { + public ContextHubInfo getContextHubInfo(int contextHubHandle) { super.getContextHubInfo_enforcePermission(); - if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) { Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo"); return null; } - return mContextHubIdToInfoMap.get(contextHubHandle); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Returns a List of ContextHubInfo object describing the available hubs. * * @return the List of ContextHubInfo objects */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public List<ContextHubInfo> getContextHubs() throws RemoteException { + public List<ContextHubInfo> getContextHubs() { super.getContextHubs_enforcePermission(); return mContextHubInfoList; @@ -814,7 +806,7 @@ public class ContextHubService extends IContextHubService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException { + public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) { super.loadNanoApp_enforcePermission(); if (mContextHubWrapper == null) { @@ -843,7 +835,7 @@ public class ContextHubService extends IContextHubService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public int unloadNanoApp(int nanoAppHandle) throws RemoteException { + public int unloadNanoApp(int nanoAppHandle) { super.unloadNanoApp_enforcePermission(); if (mContextHubWrapper == null) { @@ -870,7 +862,7 @@ public class ContextHubService extends IContextHubService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException { + public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) { super.getNanoAppInstanceInfo_enforcePermission(); @@ -880,7 +872,7 @@ public class ContextHubService extends IContextHubService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public int[] findNanoAppOnHub( - int contextHubHandle, NanoAppFilter filter) throws RemoteException { + int contextHubHandle, NanoAppFilter filter) { super.findNanoAppOnHub_enforcePermission(); @@ -895,20 +887,19 @@ public class ContextHubService extends IContextHubService.Stub { int[] retArray = new int[foundInstances.size()]; for (int i = 0; i < foundInstances.size(); i++) { - retArray[i] = foundInstances.get(i).intValue(); + retArray[i] = foundInstances.get(i); } return retArray; } /** * Performs a query at the specified hub. - * <p> - * This method should only be invoked internally by the service, either to update the service + * + * <p>This method should only be invoked internally by the service, either to update the service * cache or as a result of an explicit query requested by a client through the sendMessage API. * * @param contextHubId the ID of the hub to do the query * @return true if the query succeeded - * @throws IllegalStateException if the transaction queue is full */ private boolean queryNanoAppsInternal(int contextHubId) { if (mContextHubWrapper == null) { @@ -1003,7 +994,7 @@ public class ContextHubService extends IContextHubService.Stub { return; } - byte errorCode = ErrorCode.OK; + byte errorCode; synchronized (mReliableMessageRecordQueue) { Optional<ReliableMessageRecord> record = findReliableMessageRecord(contextHubId, @@ -1219,7 +1210,6 @@ public class ContextHubService extends IContextHubService.Stub { return mContextHubIdToInfoMap.containsKey(contextHubId); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Creates and registers a client at the service for the specified Context Hub. * @@ -1232,10 +1222,11 @@ public class ContextHubService extends IContextHubService.Stub { * @throws IllegalStateException if max number of clients have already registered * @throws NullPointerException if clientCallback is null */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public IContextHubClient createClient( int contextHubId, IContextHubClientCallback clientCallback, - @Nullable String attributionTag, String packageName) throws RemoteException { + @Nullable String attributionTag, String packageName) { super.createClient_enforcePermission(); if (!isValidContextHubId(contextHubId)) { @@ -1250,7 +1241,6 @@ public class ContextHubService extends IContextHubService.Stub { contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Creates and registers a PendingIntent client at the service for the specified Context Hub. * @@ -1262,10 +1252,11 @@ public class ContextHubService extends IContextHubService.Stub { * @throws IllegalArgumentException if hubInfo does not represent a valid hub * @throws IllegalStateException if there were too many registered clients at the service */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public IContextHubClient createPendingIntentClient( int contextHubId, PendingIntent pendingIntent, long nanoAppId, - @Nullable String attributionTag) throws RemoteException { + @Nullable String attributionTag) { super.createPendingIntentClient_enforcePermission(); if (!isValidContextHubId(contextHubId)) { @@ -1277,15 +1268,14 @@ public class ContextHubService extends IContextHubService.Stub { contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Loads a nanoapp binary at the specified Context hub. * * @param contextHubId the ID of the hub to load the binary * @param transactionCallback the client-facing transaction callback interface * @param nanoAppBinary the binary to load - * @throws IllegalStateException if the transaction queue is full */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public void loadNanoAppOnHub( int contextHubId, IContextHubTransactionCallback transactionCallback, @@ -1308,15 +1298,14 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.addTransaction(transaction); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Unloads a nanoapp from the specified Context Hub. * * @param contextHubId the ID of the hub to unload the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to unload - * @throws IllegalStateException if the transaction queue is full */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public void unloadNanoAppFromHub( int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) @@ -1333,19 +1322,17 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.addTransaction(transaction); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Enables a nanoapp at the specified Context Hub. * * @param contextHubId the ID of the hub to enable the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to enable - * @throws IllegalStateException if the transaction queue is full */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public void enableNanoApp( - int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) - throws RemoteException { + int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) { super.enableNanoApp_enforcePermission(); if (!checkHalProxyAndContextHubId( @@ -1358,19 +1345,17 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.addTransaction(transaction); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Disables a nanoapp at the specified Context Hub. * * @param contextHubId the ID of the hub to disable the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to disable - * @throws IllegalStateException if the transaction queue is full */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public void disableNanoApp( - int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) - throws RemoteException { + int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) { super.disableNanoApp_enforcePermission(); if (!checkHalProxyAndContextHubId( @@ -1383,17 +1368,16 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.addTransaction(transaction); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Queries for a list of nanoapps from the specified Context hub. * * @param contextHubId the ID of the hub to query * @param transactionCallback the client-facing transaction callback interface - * @throws IllegalStateException if the transaction queue is full */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback) - throws RemoteException { + public void queryNanoApps(int contextHubId, + IContextHubTransactionCallback transactionCallback) { super.queryNanoApps_enforcePermission(); if (!checkHalProxyAndContextHubId( @@ -1406,16 +1390,15 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.addTransaction(transaction); } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Queries for a list of preloaded nanoapp IDs from the specified Context Hub. * * @param hubInfo The Context Hub to query a list of nanoapps from. * @return The list of 64-bit IDs of the preloaded nanoapps. - * @throws NullPointerException if hubInfo is null */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override - public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException { + public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) { super.getPreloadedNanoAppIds_enforcePermission(); Objects.requireNonNull(hubInfo, "hubInfo cannot be null"); @@ -1426,7 +1409,6 @@ public class ContextHubService extends IContextHubService.Stub { return nanoappIds; } - @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) /** * Puts the context hub in and out of test mode. Test mode is a clean state * where tests can be executed in the same environment. If enable is true, @@ -1442,6 +1424,7 @@ public class ContextHubService extends IContextHubService.Stub { * test mode. * @return If true, the operation was successful; false otherwise. */ + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @Override public boolean setTestMode(boolean enable) { super.setTestMode_enforcePermission(); @@ -1551,10 +1534,6 @@ public class ContextHubService extends IContextHubService.Stub { } } - private void checkPermissions() { - ContextHubServiceUtil.checkPermissions(mContext); - } - private int onMessageReceiptOldApi( int msgType, int contextHubHandle, int appInstance, byte[] data) { if (data == null) { @@ -1586,7 +1565,6 @@ public class ContextHubService extends IContextHubService.Stub { callback.onMessageReceipt(contextHubHandle, appInstance, msg); } catch (RemoteException e) { Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ")."); - continue; } } mCallbacksList.finishBroadcast(); @@ -1729,8 +1707,8 @@ public class ContextHubService extends IContextHubService.Stub { * Hub. */ private void sendMicrophoneDisableSettingUpdateForCurrentUser() { - boolean isEnabled = mSensorPrivacyManagerInternal == null ? false : - mSensorPrivacyManagerInternal.isSensorPrivacyEnabled( + boolean isEnabled = mSensorPrivacyManagerInternal != null + && mSensorPrivacyManagerInternal.isSensorPrivacyEnabled( getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE); sendMicrophoneDisableSettingUpdate(isEnabled); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f2977082e095..a4f534eeba67 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8565,6 +8565,13 @@ public class NotificationManagerService extends SystemService { */ private boolean enqueueNotification() { synchronized (mNotificationLock) { + if (android.app.Flags.secureAllowlistToken()) { + // allowlistToken is populated by unparceling, so it will be absent if the + // EnqueueNotificationRunnable is created directly by NMS (as we do for group + // summaries) instead of via notify(). Fix that. + r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN); + } + final long snoozeAt = mSnoozeHelper.getSnoozeTimeForUnpostedNotification( r.getUser().getIdentifier(), diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0a384e530d0d..21155bb7e29e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -11154,8 +11154,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean cancel) { // This override is just for getting metrics. allFinished needs to be checked before // finish because finish resets all the states. - final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup(); - if (syncGroup != null && group != getSyncGroup()) return; + if (isDifferentSyncGroup(group)) return; mLastAllReadyAtSync = allSyncFinished(); super.finishSync(outMergedTransaction, group, cancel); } diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index e8faff621165..a8cc2ae161cf 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -348,6 +348,11 @@ class BLASTSyncEngine { wc.setSyncGroup(this); } wc.prepareSync(); + if (wc.mSyncState == WindowContainer.SYNC_STATE_NONE && wc.mSyncGroup != null) { + Slog.w(TAG, "addToSync: unset SyncGroup " + wc.mSyncGroup.mSyncId + + " for non-sync " + wc); + wc.mSyncGroup = null; + } if (mReady) { mWm.mWindowPlacerLocked.requestTraversal(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c72087bfbe5f..9b8c03809878 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -47,8 +47,6 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.SurfaceControl.METADATA_TASK_ID; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CHANGE; @@ -3586,15 +3584,29 @@ class Task extends TaskFragment { ? null : new PictureInPictureParams(top.pictureInPictureArgs); } - Rect getDisplayCutoutInsets() { - if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null; + /** @return The display cutout insets where the main window is not allowed to extend to. */ + @NonNull Rect getDisplayCutoutInsets() { + final Rect displayCutoutInsets = new Rect(); + if (mDisplayContent == null || getDisplayInfo().displayCutout == null) { + return displayCutoutInsets; + } final WindowState w = getTopVisibleAppMainWindow(); - final int displayCutoutMode = w == null - ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - : w.getAttrs().layoutInDisplayCutoutMode; - return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS - || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) - ? null : getDisplayInfo().displayCutout.getSafeInsets(); + final Rect displayFrame; + if (w != null && w.mHaveFrame) { + displayFrame = w.getDisplayFrame(); + } else { + displayFrame = mDisplayContent.getBounds(); + displayFrame.inset(getDisplayInfo().displayCutout.getSafeInsets()); + } + final Rect taskBounds = getBounds(); + if (displayCutoutInsets.setIntersect(taskBounds, displayFrame)) { + displayCutoutInsets.set( + displayCutoutInsets.left - taskBounds.left, + displayCutoutInsets.top - taskBounds.top, + taskBounds.right - displayCutoutInsets.right, + taskBounds.bottom - displayCutoutInsets.bottom); + } + return displayCutoutInsets; } /** diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 6dbd259b67c0..1f31af68c693 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3986,6 +3986,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** + * Returns {@code true} if this window container belongs to a different sync group than the + * given group. + */ + boolean isDifferentSyncGroup(@Nullable BLASTSyncEngine.SyncGroup group) { + if (group == null) return false; + final BLASTSyncEngine.SyncGroup thisGroup = getSyncGroup(); + if (thisGroup == null || group == thisGroup) return false; + Slog.d(TAG, this + " uses a different SyncGroup, current=" + thisGroup.mSyncId + + " given=" + group.mSyncId); + return true; + } + + /** * Recursively finishes/cleans-up sync state of this subtree and collects all the sync * transactions into `outMergedTransaction`. * @param outMergedTransaction A transaction to merge all the recorded sync operations into. @@ -3994,10 +4007,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group, boolean cancel) { - if (mSyncState == SYNC_STATE_NONE) return; - final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup(); - // If it's null, then we need to clean-up anyways. - if (syncGroup != null && group != syncGroup) return; + if (mSyncState == SYNC_STATE_NONE) { + if (mSyncGroup != null) { + Slog.e(TAG, "finishSync: stale group " + mSyncGroup.mSyncId + " of " + this); + mSyncGroup = null; + } + return; + } + if (isDifferentSyncGroup(group)) return; ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this); outMergedTransaction.merge(mSyncTransaction); for (int i = mChildren.size() - 1; i >= 0; --i) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index dcd4bd68c3fc..9d4a3b8b7f1c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -96,6 +96,7 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; +import static android.util.SequenceUtils.getNextSeq; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; @@ -3652,6 +3653,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } outFrames.compatScale = getCompatScaleForClient(); + outFrames.seq = getNextSeq(mLastReportedFrames.seq); if (mLastReportedFrames != outFrames) { mLastReportedFrames.setTo(outFrames); } @@ -3682,7 +3684,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) { + final int lastSeq = mLastReportedInsetsState.getSeq(); outInsetsState.set(getCompatInsetsState(), copySources); + outInsetsState.setSeq(getNextSeq(lastSeq)); if (outInsetsState != mLastReportedInsetsState) { // No need to copy for the recorded. mLastReportedInsetsState.set(outInsetsState, false /* copySources */); @@ -3691,9 +3695,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray, boolean copyControls) { + final int lastSeq = mLastReportedInsetsState.getSeq(); final InsetsSourceControl[] controls = getDisplayContent().getInsetsStateController().getControlsForDispatch(this); outArray.set(controls, copyControls); + outArray.setSeq(getNextSeq(lastSeq)); if (outArray != mLastReportedActiveControls) { // No need to copy for the recorded. mLastReportedActiveControls.setTo(outArray, false /* copyControls */); @@ -5791,8 +5797,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group, boolean cancel) { - final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup(); - if (syncGroup != null && group != syncGroup) return; + if (isDifferentSyncGroup(group)) return; mPrepareSyncSeqId = 0; if (cancel) { // This is leaving sync so any buffers left in the sync have a chance of diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp index 148054b31e89..d44bb5ae302c 100644 --- a/services/fakes/Android.bp +++ b/services/fakes/Android.bp @@ -16,5 +16,5 @@ filegroup { "java/**/*.java", ], path: "java", - visibility: ["//frameworks/base"], + visibility: ["//frameworks/base/ravenwood:__subpackages__"], } 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 4e8c75559f3b..9884085cc3f7 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -25,6 +25,8 @@ import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_D import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -1082,7 +1084,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, - UserShortcutType.HARDWARE, + HARDWARE, List.of(target), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); @@ -1346,14 +1348,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, - UserShortcutType.HARDWARE, + HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( - mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE, + mTestableContext, HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString()) ).isTrue(); } @@ -1367,7 +1369,7 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, - UserShortcutType.HARDWARE, + HARDWARE, List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); @@ -1375,7 +1377,7 @@ public class AccessibilityManagerServiceTest { assertThat( ShortcutUtils.isComponentIdExistingInSettings( mTestableContext, - ShortcutConstants.UserShortcutType.HARDWARE, + HARDWARE, TARGET_STANDARD_A11Y_SERVICE.flattenToString())) .isFalse(); } @@ -1390,14 +1392,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ true, - UserShortcutType.QUICK_SETTINGS, + QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( - mTestableContext, UserShortcutType.QUICK_SETTINGS, + mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isTrue(); verify(mStatusBarManagerInternal) @@ -1417,14 +1419,14 @@ public class AccessibilityManagerServiceTest { mA11yms.enableShortcutsForTargets( /* enable= */ false, - UserShortcutType.QUICK_SETTINGS, + QUICK_SETTINGS, List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), mA11yms.getCurrentUserIdLocked()); mTestableLooper.processAllMessages(); assertThat( ShortcutUtils.isComponentIdExistingInSettings( - mTestableContext, UserShortcutType.QUICK_SETTINGS, + mTestableContext, QUICK_SETTINGS, TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()) ).isFalse(); verify(mStatusBarManagerInternal) @@ -1614,44 +1616,49 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) - public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() { + public void restoreShortcutTargets_qs_a11yQsTargetsRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); - userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); + userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( - Settings.Secure.ACCESSIBILITY_QS_TARGETS, - /*previousValue=*/null, + ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); - assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets()) - .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile)); + Set<String> expected = Set.of(daltonizerTile, colorInversionTile); + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) + .containsExactlyElementsIn(expected); + assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) + .containsExactlyElementsIn(expected); } @Test @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) - public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() { + public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() { String daltonizerTile = AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(); String colorInversionTile = AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); - userState.updateA11yQsTargetLocked(Set.of(daltonizerTile)); + userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS); + putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); broadcastSettingRestored( - Settings.Secure.ACCESSIBILITY_QS_TARGETS, - /*previousValue=*/null, + ShortcutUtils.convertToKey(QUICK_SETTINGS), /*newValue=*/colorInversionTile); - assertThat(userState.getA11yQsTargets()) - .containsExactlyElementsIn(Set.of(daltonizerTile)); + Set<String> expected = Set.of(daltonizerTile); + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS))) + .containsExactlyElementsIn(expected); + assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS)) + .containsExactlyElementsIn(expected); } @Test @@ -1717,27 +1724,26 @@ public class AccessibilityManagerServiceTest { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE) - public void restoreA11yShortcutTargetService_targetsMerged() { + public void restoreShortcutTargets_hardware_targetsMerged() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); final String otherPrevious = TARGET_MAGNIFICATION; - final String combinedPrevious = String.join(":", servicePrevious, otherPrevious); final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); final AccessibilityUserState userState = new AccessibilityUserState( UserHandle.USER_SYSTEM, mTestableContext, mA11yms); mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); + mA11yms.enableShortcutsForTargets( + true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId); broadcastSettingRestored( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - /*previousValue=*/combinedPrevious, + ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/serviceRestored); final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored); - assertThat(readStringsFromSetting( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)) + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); - assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) - .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) + assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } @@ -1745,7 +1751,7 @@ public class AccessibilityManagerServiceTest { @EnableFlags({ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) - public void restoreA11yShortcutTargetService_alreadyHadDefaultService_doesNotClear() { + public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); mTestableContext.getOrCreateTestableResources().addOverride( R.string.config_defaultAccessibilityService, serviceDefault); @@ -1754,17 +1760,18 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); + // default is present in userState & setting, so it's not cleared + putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM); + userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); + broadcastSettingRestored( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - /*previousValue=*/serviceDefault, /*newValue=*/serviceDefault); final Set<String> expected = Set.of(serviceDefault); - assertThat(readStringsFromSetting( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)) + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); - assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) - .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) + assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } @@ -1772,7 +1779,7 @@ public class AccessibilityManagerServiceTest { @EnableFlags({ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) - public void restoreA11yShortcutTargetService_didNotHaveDefaultService_clearsDefaultService() { + public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() { final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); // Restored value from the broadcast contains both default and non-default service. @@ -1784,18 +1791,45 @@ public class AccessibilityManagerServiceTest { mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); setupShortcutTargetServices(userState); - broadcastSettingRestored( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - /*previousValue=*/null, + broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), /*newValue=*/combinedRestored); // The default service is cleared from the final restored value. final Set<String> expected = Set.of(serviceRestored); - assertThat(readStringsFromSetting( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)) + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) .containsExactlyElementsIn(expected); - assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM) - .getShortcutTargetsLocked(UserShortcutType.HARDWARE)) + assertThat(userState.getShortcutTargetsLocked(HARDWARE)) + .containsExactlyElementsIn(expected); + } + + @Test + @EnableFlags({ + android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE, + Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE}) + public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() { + final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString(); + final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(); + // Restored value from the broadcast contains both default and non-default service. + final String combinedRestored = String.join(":", serviceDefault, serviceRestored); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultAccessibilityService, serviceDefault); + final AccessibilityUserState userState = new AccessibilityUserState( + UserHandle.USER_SYSTEM, mTestableContext, mA11yms); + mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState); + setupShortcutTargetServices(userState); + + // UserState has default, but setting is null (this emulates a typical scenario in SUW). + userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE); + putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM); + + broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE), + /*newValue=*/combinedRestored); + + // The default service is cleared from the final restored value. + final Set<String> expected = Set.of(serviceRestored); + assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE))) + .containsExactlyElementsIn(expected); + assertThat(userState.getShortcutTargetsLocked(HARDWARE)) .containsExactlyElementsIn(expected); } @@ -1806,11 +1840,10 @@ public class AccessibilityManagerServiceTest { return result; } - private void broadcastSettingRestored(String setting, String previousValue, String newValue) { + private void broadcastSettingRestored(String setting, String newValue) { Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) .putExtra(Intent.EXTRA_SETTING_NAME, setting) - .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue) .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue); sendBroadcastToAccessibilityManagerService(intent); mTestableLooper.processAllMessages(); @@ -1952,4 +1985,13 @@ public class AccessibilityManagerServiceTest { private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) { return service.getCurrentUserIdLocked() == context.getUserId(); } + + private void putShortcutSettingForUser(@UserShortcutType int shortcutType, + String shortcutValue, int userId) { + Settings.Secure.putStringForUser( + mTestableContext.getContentResolver(), + ShortcutUtils.convertToKey(shortcutType), + shortcutValue, + userId); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index b269beb90c75..9fad14d889fd 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSI import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain; import static com.google.common.truth.Truth.assertThat; @@ -429,20 +430,20 @@ public class AccessibilityUserStateTest { } @Test - public void updateA11yQsTargetLocked_valueUpdated() { + public void updateShortcutTargetsLocked_quickSettings_valueUpdated() { Set<String> newTargets = Set.of( AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(), AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString() ); - mUserState.updateA11yQsTargetLocked(newTargets); + mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS); assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets); } @Test public void getA11yQsTargets_returnsCopiedData() { - updateA11yQsTargetLocked_valueUpdated(); + updateShortcutTargetsLocked_quickSettings_valueUpdated(); Set<String> targets = mUserState.getA11yQsTargets(); targets.clear(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index c1d7afb304ed..c48d745b1dc0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -14335,6 +14335,29 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN) + public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() { + Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentIntent(createPendingIntent("content")) + .build(); + NotificationRecord record = new NotificationRecord( + mContext, + new StatusBarNotification(mPkg, mPkg, 1, "tag", mUid, 44, receivedWithoutParceling, + mUser, "groupKey", 0), + mTestNotificationChannel); + assertThat(record.getNotification().getAllowlistToken()).isNull(); + + mWorkerHandler.post( + mService.new EnqueueNotificationRunnable(mUserId, record, false, false, + mPostNotificationTrackerFactory.newTracker(null))); + waitForIdle(); + + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken()) + .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN) public void enqueueNotification_rejectsOtherToken() throws RemoteException { Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setContentIntent(createPendingIntent("content")) diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java index 6c5f9752b6fc..1c32980aac91 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java @@ -33,6 +33,7 @@ import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -417,6 +418,22 @@ public class SyncEngineTests extends WindowTestsBase { } @Test + public void testSkipPrepareSync() { + final TestWindowContainer wc = new TestWindowContainer(mWm, true /* waiter */); + wc.mSkipPrepareSync = true; + final BLASTSyncEngine bse = createTestBLASTSyncEngine(); + final BLASTSyncEngine.SyncGroup syncGroup = bse.prepareSyncSet( + mock(BLASTSyncEngine.TransactionReadyListener.class), "test"); + bse.startSyncSet(syncGroup); + bse.addToSyncSet(syncGroup.mSyncId, wc); + assertEquals(SYNC_STATE_NONE, wc.mSyncState); + // If the implementation of prepareSync doesn't set sync state, the sync group should also + // be empty. + assertNull(wc.mSyncGroup); + assertTrue(wc.isSyncFinished(syncGroup)); + } + + @Test public void testNonBlastMethod() { mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow"); @@ -694,6 +711,7 @@ public class SyncEngineTests extends WindowTestsBase { final boolean mWaiter; boolean mVisibleRequested = true; boolean mFillsParent = false; + boolean mSkipPrepareSync = false; TestWindowContainer(WindowManagerService wms, boolean waiter) { super(wms); @@ -703,6 +721,9 @@ public class SyncEngineTests extends WindowTestsBase { @Override boolean prepareSync() { + if (mSkipPrepareSync) { + return false; + } if (!super.prepareSync()) { return false; } diff --git a/test-mock/Android.bp b/test-mock/Android.bp index e29d321e5105..59766579eee2 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -51,10 +51,8 @@ java_sdk_library { java_library { name: "android.test.mock.ravenwood", + defaults: ["ravenwood-internal-only-visibility-java"], srcs: [":android-test-mock-sources"], - visibility: [ - "//frameworks/base", - ], } android_ravenwood_test { |